今天业务突然来了个爬虫业务,爬出来的数据以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

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

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

记一次node爬虫经历,手把手教你爬虫的更多相关文章

  1. Python爬虫:手把手教你写迷你爬虫架构

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:我爱学Python 语言&环境 语言:继续用Python开路 ...

  2. 手把手教你写电商爬虫-第三课 实战尚妆网AJAX请求处理和内容提取

    版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 看完两篇,相信大家已经从开始的 ...

  3. 手把手教你写电商爬虫-第四课 淘宝网商品爬虫自动JS渲染

    版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 手把手教你写电商爬虫-第三课 ...

  4. 手把手教你webpack、react和node.js环境配置(上篇)

    很多人刚学习react的时候,往往因为繁琐的配置而头疼,这里我将手把手教大家怎么用webpack配置react和redux的环境,这篇教程包括前端react和后台node整个网站的环境配置,对node ...

  5. 手把手教你webpack、react和node.js环境配置(下篇)

    上篇我介绍了前端下webpack和react.redux等环境的配置,这篇将继续重点介绍后台node.js的配置. 这里是上篇链接:手把手教你webpack.react和node.js环境配置(上篇) ...

  6. [原创]手把手教你写网络爬虫(4):Scrapy入门

    手把手教你写网络爬虫(4) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 上期我们理性的分析了为什么要学习Scrapy,理由只有一个,那就是免费,一分钱都不用花! 咦?怎么有人扔西红柿 ...

  7. [原创]手把手教你写网络爬虫(5):PhantomJS实战

    手把手教你写网络爬虫(5) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 大家好!从今天开始,我要与大家一起打造一个属于我们自己的分布式爬虫平台,同时也会对涉及到的技术进行详细介绍.大 ...

  8. [原创]手把手教你写网络爬虫(7):URL去重

    手把手教你写网络爬虫(7) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 本期我们来聊聊URL去重那些事儿.以前我们曾使用Python的字典来保存抓取过的URL,目的是将重复抓取的UR ...

  9. 手把手教你写基于C++ Winsock的图片下载的网络爬虫

    手把手教你写基于C++ Winsock的图片下载的网络爬虫 先来说一下主要的技术点: 1. 输入起始网址,使用ssacnf函数解析出主机号和路径(仅处理http协议网址) 2. 使用socket套接字 ...

随机推荐

  1. 发现Xilinx Virtex 5 FPGA中单个DSP乘法器只支持17位无符号乘法

    发现Xilinx Virtex 5 FPGA中,单个DSP乘法器只支持17位无符号乘法.如果令18位乘数相乘,结果会与正确的乘积不同.

  2. Hawq架构

    Hawq采用分层架构,将MPP shared-nothing的计算层架在HDFS之上. Hawq集群中有三种角色:master, namenode和segment hosts. 1.Master负责认 ...

  3. mysql左连接查询结果不准确

    现有四张表 表(1)res_resource_catalog 表(2)res_catalog_classify 表(3)res_resource_classify 表(4)res_resource_m ...

  4. 【技术博客】 关于laravel5.1中文件上传测试的若干尝试

    关于laravel5.1中文件上传测试的若干尝试 作者:ZGJ 版本:v1.0 PM注:本人这两天也正在尝试解决这一问题,如有进展将及时更新这一博客 在我们的软工第二阶段中,我开始着手进行后端控制器的 ...

  5. [Web Pdf] flying-saucer + iText + Freemarker生成pdf 跨页问题

    转载于: https://blog.csdn.net/qq_31980421/article/details/79662988 flying-saucer + iText +  Freemarker实 ...

  6. A Survey of Visual Attention Mechanisms in Deep Learning

    A Survey of Visual Attention Mechanisms in Deep Learning 2019-12-11 15:51:59 Source: Deep Learning o ...

  7. vmware ubuntu16 启动蓝屏屏幕闪

    vmware ubuntu16 启动蓝屏屏幕闪 虚拟机安装了ubuntu16 desktop,没有关闭自动更新: 结果关机虚拟机时出现等5秒更新,类似win10关机更新: 再开机发现就蓝屏了,多次重启 ...

  8. Feign进行文件上传+表单调用

    Feigin默认是不支持文件上传和表单提交的,需要做一些配置才能支持. 1.feign依赖 图中红色为form支持必须的jar. 2.添加自定义Encoder类: import static java ...

  9. 如何设置github的ssh key

    目录 Generate SSH key in putty add key to github git setting How to add SSH key to github Generate SSH ...

  10. error: RPC failed; curl 56 GnuTLS recv error (-54): Error in the pull function.

    . . . . . 今天从 github 上 clone 代码的时候,出现了一个错误,重试多次后仍然出现,错误如下: >$ git clone https://github.com/BOINC/ ...