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 相关文章推荐
windows 下安装nodejs 环境变量设置
Feb 02 NodeJs
详解nodejs微信公众号开发——3.封装消息响应模块
Apr 10 NodeJs
NodeJs的fs读写删除移动监听
Apr 28 NodeJs
nodejs6下使用koa2框架实例
May 18 NodeJs
nodejs开发微信小程序实现密码加密
Jul 11 NodeJs
nodejs基于WS模块实现WebSocket聊天功能的方法
Jan 12 NodeJs
解决nodejs的npm命令无反应的问题
May 17 NodeJs
nodejs更新package.json中的dependencies依赖到最新版本的方法
Oct 10 NodeJs
nodejs中request库使用HTTPS代理的方法
Apr 30 NodeJs
typescript nodejs 依赖注入实现方法代码详解
Jul 21 NodeJs
NodeJS配置CORS实现过程详解
Dec 02 NodeJs
node快速搭建后台的实现步骤
Feb 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
如何解决CI框架的Disallowed Key Characters错误提示
2013/07/05 PHP
php正则匹配文章中的远程图片地址并下载图片至本地
2015/09/29 PHP
PHP面向对象学习之parent::关键字
2017/01/18 PHP
js获取height和width的方法说明
2013/01/06 Javascript
HTML5之lang属性与dir属性的详解
2013/06/19 Javascript
基于编写jQuery的无缝滚动插件
2014/08/02 Javascript
javascript实现链接单选效果的方法
2015/05/13 Javascript
JS实现复制内容到剪贴板功能兼容所有浏览器(推荐)
2016/06/17 Javascript
JS弹出窗口的运用与技巧大全
2016/11/01 Javascript
javascript跨域请求包装函数与用法示例
2016/11/03 Javascript
Reactjs实现通用分页组件的实例代码
2017/01/19 Javascript
JavaScript实现分页效果
2017/03/28 Javascript
BootStrap Table 后台数据绑定、特殊列处理、排序功能
2017/05/27 Javascript
React教程之封装一个Portal可复用组件的方法
2018/01/02 Javascript
Express本地测试HTTPS的示例代码
2018/06/06 Javascript
记React connect的几种写法(小结)
2018/09/18 Javascript
从0到1构建vueSSR项目之路由的构建
2019/03/07 Javascript
[38:51]2014 DOTA2国际邀请赛中国区预选赛 Orenda VS LGD-CDEC
2014/05/22 DOTA
[03:59]DOTA2英雄梦之声_第07期_水晶室女
2014/06/23 DOTA
[01:33]PWL开团时刻DAY2-开雾与反开雾
2020/10/31 DOTA
Python中使用urllib2防止302跳转的代码例子
2014/07/07 Python
使用python编写android截屏脚本双击运行即可
2014/07/21 Python
Python入门篇之字典
2014/10/17 Python
在Python编程过程中用单元测试法调试代码的介绍
2015/04/02 Python
简单的Python的curses库使用教程
2015/04/11 Python
Python语言生成水仙花数代码示例
2017/12/18 Python
机器学习之KNN算法原理及Python实现方法详解
2018/07/09 Python
python制作图片缩略图
2019/04/30 Python
Python进阶之迭代器与迭代器切片教程
2020/01/29 Python
django rest framework serializer返回时间自动格式化方法
2020/03/31 Python
python json.dumps中文乱码问题解决
2020/04/01 Python
Jmeter HTTPS接口测试证书导入过程图解
2020/07/22 Python
pytorch 计算Parameter和FLOP的操作
2021/03/04 Python
SISLEY希思黎官方旗舰店:享誉全球的奢华植物美容品牌
2018/04/25 全球购物
幼儿园义卖活动方案
2014/01/17 职场文书
领导干部保密承诺书
2014/08/30 职场文书