Nodejs爬虫进阶教程之异步并发控制
Nodejs爬虫进阶教程之异步并发控制
之前写了个现在看来很不完美的小爬虫,很多地方没有处理好,比如说在知乎点开一个问题的时候,它的所有回答并不是全部加载好了的,当你拉到回答的尾部时,点击加载更多,回答才会再加载一部分,所以说如果直接发送一个问题的请求链接,取得的页面是不完整的。还有就是我们通过发送链接下载图片的时候,是一张一张来下的,如果图片数量太多的话,真的是下到你睡完觉它还在下,而且我们用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请求了,去知乎看一下吧!
有了这些信息,就可以来模拟发送相同的请求来获得这些数据啦。
/*每隔毫秒模拟发送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为空时,我们就要对这些图片进行下载了,不可能一条一条下对不对,因为如你所看到的,我们的图片足足有
没错,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("全部已下载完毕!"); } }); };
先看这里=>
mapLimit方法的第一个参数photos是所有图片链接的数组,也是我们并发请求的对象,asyncNum是限制并发请求的数量,如果没有这个参数的话,将会有同时两万多条请求发送过去,嗯,你的ip就会被成功的封掉,但当我们有这个参数时,比如它的值是10,则它一次就只会帮我们从数组中取10条链接,执行并发的请求,这10条请求都得到响应后,再发送下10条请求。告诉泥萌,并发到同时100条没有事的,下载速度超级快,再往上就不知道咯,你们来告诉我...
以上所述给大家介绍了Nodejs爬虫进阶教程之异步并发控制的相关知识,希望对大家有所帮助。
Nodejs爬虫进阶教程之异步并发控制的更多相关文章
- Nodejs爬虫进阶=>异步并发控制
之前写了个现在看来很不完美的小爬虫,很多地方没有处理好,比如说在知乎点开一个问题的时候,它的所有回答并不是全部加载好了的,当你拉到回答的尾部时,点击加载更多,回答才会再加载一部分,所以说如果直接发送一 ...
- 爬虫进阶教程:极验(GEETEST)验证码破解教程
摘要 爬虫最大的敌人之一是什么?没错,验证码!Geetest作为提供验证码服务的行家,市场占有率还是蛮高的.遇到Geetest提供的滑动验证码怎么破?授人予鱼不如授人予渔,接下来就为大家呈现本教程的精 ...
- Python爬虫与数据分析之进阶教程:文件操作、lambda表达式、递归、yield生成器
专栏目录: Python爬虫与数据分析之python教学视频.python源码分享,python Python爬虫与数据分析之基础教程:Python的语法.字典.元组.列表 Python爬虫与数据分析 ...
- <node.js爬虫>制作教程
前言:最近想学习node.js,突然在网上看到基于node的爬虫制作教程,所以简单学习了一下,把这篇文章分享给同样初学node.js的朋友. 目标:爬取 http://tweixin.yueyishu ...
- NodeJS 爬虫爬取LOL英雄联盟的英雄信息,批量下载英雄壁纸
工欲善其事,必先利其器,会用各种模块非常重要. 1.模块使用 (1)superagent:Nodejs中的http请求库(每个语言都有无数个,java的okhttp,OC的afnetworking) ...
- Android高手进阶教程(二十八)之---Android ViewPager控件的使用(基于ViewPager的横向相册)!!!
分类: Android高手进阶 Android基础教程 2012-09-14 18:10 29759人阅读 评论(35) 收藏 举报 android相册layoutobjectclassloade ...
- NodeJS爬虫系统初探
NodeJS爬虫系统 NodeJS爬虫系统 0. 概论 爬虫是一种自动获取网页内容的程序.是搜索引擎的重要组成部分,因此搜索引擎优化很大程度上是针对爬虫而做出的优化. robots.txt是一个文本文 ...
- nodejs爬虫——汽车之家所有车型数据
应用介绍 项目Github地址:https://github.com/iNuanfeng/node-spider/ nodejs爬虫,爬取汽车之家(http://www.autohome.com.cn ...
- Python爬虫入门教程 37-100 云沃客项目外包网数据爬虫 scrapy
爬前叨叨 2019年开始了,今年计划写一整年的博客呢~,第一篇博客写一下 一个外包网站的爬虫,万一你从这个外包网站弄点外快呢,呵呵哒 数据分析 官方网址为 https://www.clouderwor ...
随机推荐
- URL编码 URLEncoder 示例
2016-12-27 对字符编码时的规则 通常如果一样东西需要编码,说明这样东西并不适合传输.原因多种多样,如Size过大,包含隐私数据. 对于Url来说,之所以要进行编码,一个是因为Url中有些字符 ...
- Axiom3D学习日记 0.Axiom基础知识
Axiom 3D Engine An open-source, cross-platform, managed 3D rendering engine for DirectX, XNA and Ope ...
- Mongodb 启动时 lock文件访问没有权限处理
mongodb 第二次启动时候异常信息: lock file: /data/db/mongod.lock errno:13 Permission denied Is a mongod instance ...
- 用PHP实现一个高效安全的ftp服务器(一)
摘要: 本文主要阐述使用PHP的swoole扩展实现ftp服务器,同时扩展ftp服务器个性化功能和安全性.真正实现一个自己完全掌控的ftp服务器,可以个性化定制的ftp服务器. 正文: FTP服务器想 ...
- iOS6以后的单个控制器横竖屏显示以及旋转屏控制技巧,附带iOS8以后显示电池状态栏
一.在应用中从竖屏模式强制转换为横屏模式 第一种方法:通过模态弹出视图的方式,使得特定ViewController坚持特定的interfaceOrientation(1)iOS6之后提供了这样一个方法 ...
- 谢尔排序/缩减增量排序(C++)
谢尔排序/缩减增量排序(C++) 谢尔排序/缩减增量排序: 他通过比较相距一定间隔的元素来工作,各趟比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止.(好复杂) 看了一下实现代 ...
- 【CF39E】【博弈论】What Has Dirichlet Got to Do with That?
Description You all know the Dirichlet principle, the point of which is that if n boxes have no less ...
- 第17章课后题(高级Perl技巧)
17.1 写一个程序,从文件中读取一组字符串(每行一个),然后让用户键入模式以便进行字符串匹配. 对于每个模式,程序应该说明文件里共有多少字符串匹配成功,分别是哪些字符串. 对于所键入的每个新模式,不 ...
- directsound 应用实例
sdk里边有个文件夹Samples\C++\XInput\AudioController这个就是
- ExtJs中动态加载机制研究(转)
觉得写的太好了,怕弄丢了,转一下:http://extjs.org.cn/node/659 昨天我们team对于extjs的动态加载机制做了些深入研究,这里先share下controller加载的结果 ...