今天业务突然来了个爬虫业务,爬出来的数据以Excel的形式导出,下班前一个小时开始做,加班一个小时就做好了。因为太久没做爬虫了!做这个需求都是很兴奋!

需求说明

  1. 访问网站
  2. (循环)获取页面指定数据源
  3. 根据页面数据源再(循环)访问详情数据
  4. 记录详情数据,以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();

总结

其实爬虫就是:

  1. 模拟浏览器请求,获取到HTML
  2. 对HTML做解析,将需要数据提取出来
  3. 把数据进一步处理,导出Excel,保存数据库等等

最后

其实这个爬虫最终是

  1. 循环访问带有分页的表格
  2. 提取表格的链接并访问链接 去到详情页
  3. 在详情页获取到我所需要的数据
  4. 最终输出Excel

但我在这里就写了获取各页表格里的链接地址,因为在这里我只想做一个简单的分享。

这些分享应该都足以触类旁通了。

最新文章

  1. IOS第18天(1,核心动画layer, 旋转,缩放,平移,边框,剪裁,圆角)
  2. iOS-APP发布应注意要点
  3. 用LoadRunner实现接口测试
  4. PHP之set_error_handler()函数讲解
  5. [CUDA] ubuntu14.04+cuda7.5下安装cudnn7.0
  6. 【转】Xcode7.1环境下上架iOS App到AppStore 流程 (Part 三)
  7. 【转】《分享一下我研究SQLSERVER以来收集的笔记》未整理
  8. POJ-1700 Crossing River---过河问题(贪心)
  9. 基于vue-simple-uploader封装文件分片上传、秒传及断点续传的全局上传插件
  10. wp系统笔记
  11. Halcon 标定与准确测量
  12. Bootstrap-table使用总结(整合版)
  13. Flask中无法在其他函数中查询Sqlachemy的解决办法
  14. hadoop 管理命令dfsadmin
  15. VS2015 代码片段整理
  16. 漏洞复现——Apache SSI远程命令执行
  17. postgres on linux red hat 7 配置问题
  18. Ansible基础概述
  19. asp.net 获取客户端IP
  20. TCP/IP三次握手四次挥手分析

热门文章

  1. OpenVSwitch
  2. HGNC数据库 HUGO基因命名委员会
  3. 关于conda和jupyter使用
  4. How to Use Convolutional Neural Networks for Time Series Classification
  5. java上传图片并压缩图片大小
  6. ESB企业服务总线到底是什么东西呢?
  7. ES开启慢查询日志
  8. 基于redis5的session共享:【redis 5.x集群应用研究】
  9. 使用 Laravel-Swagger 编写接口文档(php)
  10. osg geometry清空vertex