记一次node爬虫经历,手把手教你爬虫
今天业务突然来了个爬虫业务,爬出来的数据以Excel的形式导出,下班前一个小时开始做,加班一个小时就做好了。因为太久没做爬虫了!做这个需求都是很兴奋!
需求说明
- 访问网站
- (循环)获取页面指定数据源
- 根据页面数据源再(循环)访问详情数据
- 记录详情数据,以Excel形式导出。
所需模块
根据需求所得五个模块
// 请求模块(1.访问网站)
const request = require('request');
// 可以看做成node版的jQuery(2.获取页面指定数据源)
const cheerio = require("cheerio");
// node异步流程控制 异步循环(3.根据页面数据源再访问详情数据)
const async = require("async");
// Excel表格导出+node自带文件系统(4.以Excel形式导出)
const excelPort = require('excel-export');
const fs = require("fs");
安装模块:
npm install request cheerio async excel-export --save-dev
开始发送请求
一开始我直接用request请求网站,但直接返回了404,但我在浏览器上看又是没毛病的。然后我就改了下请求的header。嘻嘻
request({
url: 'http://www.foo.cn?page=1',
method: 'get',
headers: {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
// 这里巨坑!这里开启了gzip的话http返回来的是Buffer。
// 'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cache-Control': 'no-cache',
},
// 想请求回来的html不是乱码的话必须开启encoding为null
encoding: null
}, (err, res, body) => {
// 这样就可以直接获取请求回来html了
console.log('打印HTML', body.toString()); // <html>xxxx</html>
}
);
获取指定数据源
request({
url: 'http://www.foo.cn?page=1',
method: 'get',
headers: {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
// 'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cache-Control': 'no-cache',
},
encoding: null
}, (err, res, body) => {
console.log('打印HTML', body.toString()); // <html>xxxx</html>
const list = [];
const $ = cheerio.load(body.toString());
// 获取指定元素
let item = $('.className tbody tr');
// 循环得到元素的跳转地址和名称
item.map((i, index) => {
let obj = {};
obj.link = $(index).children('a').attr('href');
obj.name = $(index).children('a').text();
list.push(obj);
});
console.log('list', list); // [{ link: 'http://xxxx.com', name: 'abc' }]
}
);
异步流程控制
先将request封装多一层,传入page值和async.series的callback
async function requestPage(page = 1, callback) {
request({
url: 'http://www.masuma.cn/product.php?lm=21&page=' + page,
method: 'get',
headers: {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
// 'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cache-Control': 'no-cache',
},
encoding: null
}, async (err, res, body) => {
console.log('打印HTML', body.toString()); // <html>xxxx</html>
const list = [];
const $ = cheerio.load(body.toString());
// 获取指定元素
let item = $('.className tbody tr');
// 循环得到元素的跳转地址和名称
item.map((i, index) => {
let obj = {};
obj.link = $(index).children('a').attr('href');
obj.name = $(index).children('a').text();
list.push(obj);
});
console.log('list', list); // [{ link: 'http://xxxx.com', name: 'abc' }]
callback(null, list);
}
);
}
打印出数据 + 导出Excel
async function main() {
const requestList = [];
// 在这里为什么要用到async.series?
// 是因为这个爬虫需要具有顺序性,必须得异步请求完一个地址并获取数据然后再存到一个变量里才能执行下一个
// 在此期间我想过其他方法。例如:
// for循环 + await 直接否定了
// Promise.all这个并不能保证数据具有顺序
// 最终敲定用async.series 用完之后!真香啊!
// 很好奇async.series找个时间也做个源码解析
for (let i = 1; i < 36; i++) {
requestList.push(callback => {
requestPage(i, callback);
});
}
console.log('requestList', requestList); // [Function, Function] 全是function的数组
async.series(requestList, (err, result) => {
// 因为async.series返回来的结果是[[], [], []]这种二维数组形式,每个function返回来的值都放在一个数组里,我们需要将它弄成一维数组好做导出列表
const arry = [].concat.apply([], result);
console.log('最终结果!!!!', arry); // [{ link: 'http://xxxx.com', name: 'abc' }, ...]
writeExcel(arry);
});
}
const writeExcel = (datas) => {
// 定义一个对象,存放内容
let conf = {};
// 定义表头
conf.cols = [
{caption:'玛速玛编码', type:'string', width:40},
{caption:'原厂编码', type:'string', width:60},
];
// 创建一个数组用来多次遍历行数据
let array = [];
// 循环导入从传参中获取的表内容
for (let i=0;i<datas.length;i++){
//依次写入
array[i] = [
datas[i].name,
datas[i].code,
];
}
// 写入道conf对象中
conf.rows = array;
// 生成表格
const result = excelPort.execute(conf);
// 定义表格存放路径
fs.writeFile('./表格.xlsx', result, 'binary',function(err){
if(err){
console.log(err);
}
});
}
main();
总结
其实爬虫就是:
- 模拟浏览器请求,获取到HTML
- 对HTML做解析,将需要数据提取出来
- 把数据进一步处理,导出Excel,保存数据库等等
最后
其实这个爬虫最终是
- 循环访问带有分页的表格
- 提取表格的链接并访问链接 去到详情页
- 在详情页获取到我所需要的数据
- 最终输出Excel
但我在这里就写了获取各页表格里的链接地址,因为在这里我只想做一个简单的分享。
这些分享应该都足以触类旁通了。
记一次node爬虫经历,手把手教你爬虫的更多相关文章
- Python爬虫:手把手教你写迷你爬虫架构
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:我爱学Python 语言&环境 语言:继续用Python开路 ...
- 手把手教你写电商爬虫-第三课 实战尚妆网AJAX请求处理和内容提取
版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 看完两篇,相信大家已经从开始的 ...
- 手把手教你写电商爬虫-第四课 淘宝网商品爬虫自动JS渲染
版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 手把手教你写电商爬虫-第三课 ...
- 手把手教你webpack、react和node.js环境配置(上篇)
很多人刚学习react的时候,往往因为繁琐的配置而头疼,这里我将手把手教大家怎么用webpack配置react和redux的环境,这篇教程包括前端react和后台node整个网站的环境配置,对node ...
- 手把手教你webpack、react和node.js环境配置(下篇)
上篇我介绍了前端下webpack和react.redux等环境的配置,这篇将继续重点介绍后台node.js的配置. 这里是上篇链接:手把手教你webpack.react和node.js环境配置(上篇) ...
- [原创]手把手教你写网络爬虫(4):Scrapy入门
手把手教你写网络爬虫(4) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 上期我们理性的分析了为什么要学习Scrapy,理由只有一个,那就是免费,一分钱都不用花! 咦?怎么有人扔西红柿 ...
- [原创]手把手教你写网络爬虫(5):PhantomJS实战
手把手教你写网络爬虫(5) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 大家好!从今天开始,我要与大家一起打造一个属于我们自己的分布式爬虫平台,同时也会对涉及到的技术进行详细介绍.大 ...
- [原创]手把手教你写网络爬虫(7):URL去重
手把手教你写网络爬虫(7) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 本期我们来聊聊URL去重那些事儿.以前我们曾使用Python的字典来保存抓取过的URL,目的是将重复抓取的UR ...
- 手把手教你写基于C++ Winsock的图片下载的网络爬虫
手把手教你写基于C++ Winsock的图片下载的网络爬虫 先来说一下主要的技术点: 1. 输入起始网址,使用ssacnf函数解析出主机号和路径(仅处理http协议网址) 2. 使用socket套接字 ...
随机推荐
- javaScript 迭代器
for ...of 语句 "use strict"; var name = ['a','b','c']; var mark = [1, 2, 3]; for(var i of na ...
- 【XR-4】题
题面 题解 由题,所求为方程\(y^2 = x^2 + ax + b\)的整数解数量. 两边同乘\(4\),可得\((2y)^2 = 4x^2 + 4ax + 4b\). 配方后得\((2y)^2 = ...
- yum本地安装rrdtool报错,提示版本冲突的解决办法
[root@mysql-mon40 mm]# yum localinstall rrdtool-1.4.7-1.el6.rfx.x86_64.rpm -y 错误: Multilib version p ...
- PSQLException: An I/O error occurred while sending to the backend.
postgresql批量新增数据时,批量插入的数据量太大了,造成了IO异常 只能把这么多数据先进行分割,再批量插入,但是感觉这种方式不是最优解,先暂时顶着,具体List分割方式如下: package ...
- fiddler自动生成jmeter测试脚本
概述 昨天我们在课堂上讲了如何通过fiddler抓包,单一接口可以复制到jmeter中进行接口测试,那么如果抓包获取了大量的接口,我们如何快速实现接口转换成jmx文件呢? 今天给大家介绍fiddler ...
- 深入分析Synchronized原理(阿里面试题)
还有一篇 讲解lock的实现原理,参考:解决多线程安全问题-无非两个方法synchronized和lock 具体原理以及如何 获取锁AQS算法 (百度-美团) 记得开始学习Java的时候,一遇到多线程 ...
- 【转】linux sed命令
转自:linux sed命令就是这么简单 参考:Linux三大剑客之sed:https://blog.csdn.net/solaraceboy/article/details/79272344 阅读目 ...
- linux 定时任务 cron,利用cron进程保活
cron定时任务 crond服务操作命令 /sbin/service crond start //启动服务 /sbin/service crond stop //关闭服务 /sbin/service ...
- C#中得到每周,每月,每季,每年的年初末日期
DateTime表示时间上的一刻,通常以日期和当天的时间表示.借用这个结构,我们可以实现较丰富的功能,本文给出得到每周每天的方法,及得到本月第一天,本月最后一天,本季第一天,本季最后一天,本年第一天及 ...
- 解决Linux系统下面javamelody图片中文乱码问题
从windows系统中,copy了C:\Windows\Fonts\msyh.ttc和msyhbd.ttc 2个文件到 服务器的%JAVA_HOME%\jre\lib\fonts\fallback 目 ...