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开发微博实例
Mar 25 NodeJs
实例分析nodejs模块xml2js解析xml过程中遇到的坑
Mar 18 NodeJs
NodeJS学习笔记之Module的简介
Mar 24 NodeJs
NodeJs安装npm包一直失败的解决方法
Apr 28 NodeJs
关于Mac下安装nodejs、npm和cnpm的教程
Apr 11 NodeJs
nodejs更改项目端口号的方法
May 13 NodeJs
NVM安装nodejs的方法实用步骤
Jan 16 NodeJs
Nodejs监听日志文件的变化的过程解析
Aug 04 NodeJs
nodejs dgram模块广播+组播的实现示例
Nov 04 NodeJs
NodeJS实现一个聊天室功能
Nov 25 NodeJs
Sublime Text3 配置 NodeJs 环境的方法
May 20 NodeJs
用Nodejs实现在终端中炒股的实现
Oct 18 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
我的群发邮件程序
2006/10/09 PHP
PHP 飞信好友免费短信API接口开源版
2010/07/22 PHP
PHP变量内存分配问题记录整理
2013/11/27 PHP
php制作中间带自己定义图片二维码的方法
2014/01/27 PHP
PHP 面向对象程序设计(oop)学习笔记 (五) - PHP 命名空间
2014/06/12 PHP
php判断两个浮点数是否相等的方法
2015/03/14 PHP
各浏览器对link标签onload/onreadystatechange事件支持的差异分析
2011/04/27 Javascript
JavaScript解析json格式数据简单示例
2014/12/09 Javascript
jQuery+ajax实现动态执行脚本的方法
2015/01/27 Javascript
jQuery实现类似淘宝网图片放大效果的方法
2015/07/08 Javascript
jquery.Jcrop结合JAVA后台实现图片裁剪上传实例
2016/11/05 Javascript
echarts3 使用总结(绘制各种图表,地图)
2017/01/05 Javascript
nodejs个人博客开发第二步 入口文件
2017/04/12 NodeJs
JavaScript判断浏览器和hack滚动条的写法
2017/07/23 Javascript
详解JavaScript中的Object.is()与&quot;===&quot;运算符总结
2020/06/17 Javascript
解决vant title-active-color与title-inactive-color不生效问题
2020/11/03 Javascript
[00:33]DOTA2上海特级锦标赛 CDEC战队宣传片
2016/03/04 DOTA
[01:33]PWL开团时刻DAY2-开雾与反开雾
2020/10/31 DOTA
python3 selenium 切换窗口的几种方法小结
2018/05/21 Python
python用户评论标签匹配的解决方法
2018/05/31 Python
通过实例解析python描述符原理作用
2020/01/22 Python
pycharm第三方库安装失败的问题及解决经验分享
2020/05/09 Python
通过Python实现一个简单的html页面
2020/05/16 Python
利用CSS3动画实现圆圈由小变大向外扩散的效果实例
2018/09/10 HTML / CSS
这76道Java面试题及答案,祝你能成功通过面试
2016/04/16 面试题
宣传策划类求职信范文
2014/01/31 职场文书
汽车广告策划方案
2014/05/31 职场文书
2014教师专业技术工作总结
2014/12/03 职场文书
学校运动会简讯
2015/07/20 职场文书
学习雷锋主题班会
2015/08/14 职场文书
pytorch锁死在dataloader(训练时卡死)
2021/05/28 Python
Python OpenCV实现传统图片格式与base64转换
2021/06/13 Python
css3中2D转换之有趣的transform形变效果
2022/02/24 HTML / CSS
十大经典日本动漫排行榜 海贼王第三,犬夜叉仅第八
2022/03/18 日漫
《艾尔登法环》发布最新「战技」宣传片
2022/04/03 其他游戏
Linux、ubuntu系统下查看显卡型号、显卡信息详解
2022/04/07 Servers