Nodejs爬虫进阶教程之异步并发控制


Posted in NodeJs onFebruary 15, 2016

之前写了个现在看来很不完美的小爬虫,很多地方没有处理好,比如说在知乎点开一个问题的时候,它的所有回答并不是全部加载好了的,当你拉到回答的尾部时,点击加载更多,回答才会再加载一部分,所以说如果直接发送一个问题的请求链接,取得的页面是不完整的。还有就是我们通过发送链接下载图片的时候,是一张一张来下的,如果图片数量太多的话,真的是下到你睡完觉它还在下,而且我们用nodejs写的爬虫,却竟然没有用到nodejs最牛逼的异步并发的特性,太浪费了啊。

思路

这次的的爬虫是上次那个的升级版,不过呢,上次那个虽然是简单,但是很适合新手学习啊。这次的爬虫代码在我的github上可以找到=>NodeSpider。

整个爬虫的思路是这样的:在一开始我们通过请求问题的链接抓取到部分页面数据,接下来我们在代码中模拟ajax请求截取剩余页面的数据,当然在这里也是可以通过异步来实现并发的,对于小规模的异步流程控制,可以用这个模块=>eventproxy,但这里我就没有用啦!我们通过分析获取到的页面从中截取出所有图片的链接,再通过异步并发来实现对这些图片的批量下载。

抓取页面初始的数据很简单啊,这里就不做多解释啦

/*获取首屏所有图片链接*/
var getInitUrlList=function(){
request.get("https://www.zhihu.com/question/")
.end(function(err,res){
if(err){
console.log(err);
}else{
var $=cheerio.load(res.text);
var answerList=$(".zm-item-answer");
answerList.map(function(i,answer){
var images=$(answer).find('.zm-item-rich-text img');
images.map(function(i,image){
photos.push($(image).attr("src"));
});
});
console.log("已成功抓取"+photos.length+"张图片的链接");
getIAjaxUrlList();
}
});
}

模拟ajax请求获取完整页面

接下来就是怎么去模拟点击加载更多时发出的ajax请求了,去知乎看一下吧!

Nodejs爬虫进阶教程之异步并发控制

Nodejs爬虫进阶教程之异步并发控制

Nodejs爬虫进阶教程之异步并发控制

有了这些信息,就可以来模拟发送相同的请求来获得这些数据啦。

/*每隔毫秒模拟发送ajax请求,并获取请求结果中所有的图片链接*/
var getIAjaxUrlList=function(offset){
request.post("https://www.zhihu.com/node/QuestionAnswerListV")
.set(config)
.send("method=next¶ms=%B%url_token%%A%C%pagesize%%A%C%offset%%A" +offset+ "%D&_xsrf=adfdeee")
.end(function(err,res){
if(err){
console.log(err);
}else{
var response=JSON.parse(res.text);/*想用json的话对json序列化即可,提交json的话需要对json进行反序列化*/
if(response.msg&&response.msg.length){
var $=cheerio.load(response.msg.join(""));/*把所有的数组元素拼接在一起,以空白符分隔,不要这样join(),它会默认数组元素以逗号分隔*/
var answerList=$(".zm-item-answer");
answerList.map(function(i,answer){
var images=$(answer).find('.zm-item-rich-text img');
images.map(function(i,image){
photos.push($(image).attr("src"));
});
});
setTimeout(function(){
offset+=;
console.log("已成功抓取"+photos.length+"张图片的链接");
getIAjaxUrlList(offset);
},);
}else{
console.log("图片链接全部获取完毕,一共有"+photos.length+"条图片链接");
// console.log(photos);
return downloadImg();
}
}
});
}

在代码中post这条请求https://www.zhihu.com/node/QuestionAnswerListV2,把原请求头和请求参数复制下来,作为我们的请求头和请求参数,superagent的set方法可用来设置请求头,send方法可以用来发送请求参数。我们把请求参数中的offset初始为20,每隔一定时间offset再加20,再重新发送请求,这样就相当于我们每隔一定时间发送了一条ajax请求,获取到最新的20条数据,每获取到了数据,我们再对这些数据进行一定的处理,让它们变成一整段的html,便于后面的提取链接处理。 异步并发控制下载图片再获取完了所有的图片链接之后,即判定response.msg为空时,我们就要对这些图片进行下载了,不可能一条一条下对不对,因为如你所看到的,我们的图片足足有

Nodejs爬虫进阶教程之异步并发控制

没错,2万多张,不过幸好nodejs拥有神奇的单线程异步特性,我们可以同时对这些图片进行下载。但这个时候问题来了,听说同时发送请求太多的话会被网站封ip哒!这是真的吗?我不知道啊,没试过,因为我也不想去试( ̄? ̄〃),所以这个时候我们就需要对异步并发数量进行一些控制了。

在这里用到了一个神奇的模块=>async,它不仅能帮我们拜托难以维护的回调金字塔恶魔,还能轻松的帮我们进行异步流程的管理。具体看文档啦,因为我自己也不怎么会用,这里就只用到了一个强大的async.mapLimit方法。真的很厉害哦。

var requestAndwrite=function(url,callback){
request.get(url).end(function(err,res){
if(err){
console.log(err);
console.log("有一张图片请求失败啦...");
}else{
var fileName=path.basename(url);
fs.writeFile("./img/"+fileName,res.body,function(err){
if(err){
console.log(err);
console.log("有一张图片写入失败啦...");
}else{
console.log("图片下载成功啦");
callback(null,"successful !");
/*callback貌似必须调用,第二个参数将传给下一个回调函数的result,result是一个数组*/
}
});
}
});
}
var downloadImg=function(asyncNum){
/*有一些图片链接地址不完整没有“http:”头部,帮它们拼接完整*/
for(var i=;i<photos.length;i++){
if(photos[i].indexOf("http")===-){
photos[i]="http:"+photos[i];
}
}
console.log("即将异步并发下载图片,当前并发数为:"+asyncNum);
async.mapLimit(photos,asyncNum,function(photo,callback){
console.log("已有"+asyncNum+"张图片进入下载队列");
requestAndwrite(photo,callback);
},function(err,result){
if(err){
console.log(err);
}else{
// console.log(result);<=会输出一个有万多个“successful”字符串的数组
console.log("全部已下载完毕!");
}
});
};

先看这里=>

Nodejs爬虫进阶教程之异步并发控制

mapLimit方法的第一个参数photos是所有图片链接的数组,也是我们并发请求的对象,asyncNum是限制并发请求的数量,如果没有这个参数的话,将会有同时两万多条请求发送过去,嗯,你的ip就会被成功的封掉,但当我们有这个参数时,比如它的值是10,则它一次就只会帮我们从数组中取10条链接,执行并发的请求,这10条请求都得到响应后,再发送下10条请求。告诉泥萌,并发到同时100条没有事的,下载速度超级快,再往上就不知道咯,你们来告诉我...

以上所述给大家介绍了Nodejs爬虫进阶教程之异步并发控制的相关知识,希望对大家有所帮助。

NodeJs 相关文章推荐
Nodejs中读取中文文件编码问题、发送邮件和定时任务实例
Jan 01 NodeJs
nodejs中实现阻塞实例
Mar 24 NodeJs
nodejs个人博客开发第五步 分配数据
Apr 12 NodeJs
nodejs socket实现的服务端和客户端功能示例
Jun 02 NodeJs
nodejs模块学习之connect解析
Jul 05 NodeJs
NodeJs form-data格式传输文件的方法
Dec 13 NodeJs
nodejs实现OAuth2.0授权服务认证
Dec 27 NodeJs
Nodejs连接mysql并实现增、删、改、查操作的方法详解
Jan 04 NodeJs
nodejs实现的简单web服务器功能示例
Mar 15 NodeJs
NodeJs操作MongoDB教程之分页功能以及常见问题
Apr 09 NodeJs
nodejs中使用archive压缩文件的实现代码
Nov 26 NodeJs
nodejs+express最简易的连接数据库的方法
Dec 23 NodeJs
你一定会收藏的Nodejs代码片段
Feb 04 #NodeJs
Nodejs中session的简单使用及通过session实现身份验证的方法
Feb 04 #NodeJs
nodejs实现bigpipe异步加载页面方案
Jan 26 #NodeJs
NodeJS实现阿里大鱼短信通知发送
Jan 17 #NodeJs
实例详解Nodejs 保存 payload 发送过来的文件
Jan 14 #NodeJs
Nodejs express框架一个工程中同时使用ejs模版和jade模版
Dec 28 #NodeJs
深入浅析NodeJs并发异步的回调处理
Dec 21 #NodeJs
You might like
PHP制作百度词典查词采集器
2015/01/29 PHP
浅析PHP中json_encode与json_decode的区别
2020/07/15 PHP
js中将URL中的参数提取出来作为对象的实现代码
2011/08/16 Javascript
原生js 秒表实现代码
2012/07/24 Javascript
THREE.JS入门教程(4)创建粒子系统
2013/01/24 Javascript
关于JavaScript中name的意义冲突示例介绍
2014/05/29 Javascript
jquery 属性选择器(匹配具有指定属性的元素)
2016/09/06 Javascript
使用Javascript判断浏览器终端设备(PC、IOS(iphone)、Android)
2017/01/04 Javascript
jQuery、zepto、js常用小技巧
2017/02/12 Javascript
jquery仿苹果的时间/日期选择效果
2017/03/08 Javascript
jQuery动态添加.active 实现导航效果代码思路详解
2017/08/29 jQuery
详解angular脏检查原理及伪代码实现
2018/06/08 Javascript
JavaScript设计模式之缓存代理模式原理与简单用法示例
2018/08/07 Javascript
解决vue接口数据赋值给data没有反应的问题
2018/08/27 Javascript
js canvas画布实现高斯模糊效果
2018/11/27 Javascript
js实现前端界面导航栏下拉列表
2020/08/27 Javascript
Vue3不支持Filters过滤器的问题
2020/09/24 Javascript
[03:49]显微镜下的DOTA2第十五期—VG登基之路完美团
2014/06/24 DOTA
[06:45]2018DOTA2亚洲邀请赛 4.5 SOLO赛 Sccc vs Maybe
2018/04/06 DOTA
[56:20]LGD vs VP Supermajor 败者组决赛 BO3 第三场 6.10
2018/07/04 DOTA
Python中使用PyHook监听鼠标和键盘事件实例
2014/07/18 Python
Python爬虫实例_利用百度地图API批量获取城市所有的POI点
2018/01/10 Python
超简单使用Python换脸实例
2019/03/27 Python
python 爬取B站原视频的实例代码
2020/09/09 Python
ffmpeg+Python实现B站MP4格式音频与视频的合并示例代码
2020/10/21 Python
HTTP状态码详解
2021/03/18 杂记
Brasty波兰:香水、化妆品、手表网上商店
2019/04/15 全球购物
2013的个人自我评价
2013/12/26 职场文书
求职信范文英文版
2014/01/05 职场文书
新品发布会策划方案
2014/06/08 职场文书
领导干部整治奢华浪费之风思想汇报
2014/10/07 职场文书
玩手机检讨书1000字
2014/10/20 职场文书
指导老师鉴定意见
2015/06/05 职场文书
员工安全责任协议书
2016/03/22 职场文书
html form表单基础入门案例讲解
2021/07/15 HTML / CSS
HTML CSS 一个标签实现带动画的抖音LOGO
2022/04/26 HTML / CSS