nodejs抓取动态网页(异步特性()(2)_知乎点开(图))

优采云 发布时间: 2021-10-21 07:02

  nodejs抓取动态网页(异步特性()(2)_知乎点开(图))

  写了个小爬虫,貌似很不完善。很多地方都没有处理。例如,当在 知乎 中打开一个问题时,并不是所有的答案都被加载。当你拉到答案的结尾时,点击加载更多来加载一部分答案,所以如果你直接发送一个问题的请求链接,你得到的页面是不完整的。还有就是我们通过访问链接下载图片的时候,是一张一张的下载的。如果图片太多,真的会下载到你睡着了。

  这个爬虫是上一个的升级版。爬虫代码可以在我的github=>NodeSpider上找到。

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

  抓取页面的初始数据很简单,这里就不解释了。

   1 /*获取首屏所有图片链接*/

2 var getInitUrlList=function(){

3 request.get("https://www.zhihu.com/question/34937418")

4 .end(function(err,res){

5 if(err){

6 console.log(err);

7 }else{

8 var $=cheerio.load(res.text);

9 var answerList=$(".zm-item-answer");

10 answerList.map(function(i,answer){

11 var images=$(answer).find('.zm-item-rich-text img');

12 images.map(function(i,image){

13 photos.push($(image).attr("src"));

14 });

15 });

16 console.log("已成功抓取"+photos.length+"张图片的链接");

17 getIAjaxUrlList(20);

18 }

19 });

20 }

  模拟ajax请求获取完整页面

  下一步是如何模拟点击加载更多时发送的ajax请求。去知乎看看吧!

  

  

  

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

   1 /*每隔100毫秒模拟发送ajax请求,并获取请求结果中所有的图片链接*/

2 var getIAjaxUrlList=function(offset){

3 request.post("https://www.zhihu.com/node/QuestionAnswerListV2")

4 .set(config)

5 .send("method=next&params=%7B%22url_token%22%3A34937418%2C%22pagesize%22%3A20%2C%22offset%22%3A" +offset+ "%7D&_xsrf=98360a2df02783902146dee374772e51")

6 .end(function(err,res){

7 if(err){

8 console.log(err);

9 }else{

10 var response=JSON.parse(res.text);/*想用json的话对json序列化即可,提交json的话需要对json进行反序列化*/

11 if(response.msg&&response.msg.length){

12 var $=cheerio.load(response.msg.join(""));/*把所有的数组元素拼接在一起,以空白符分隔,不要这样join(),它会默认数组元素以逗号分隔*/

13 var answerList=$(".zm-item-answer");

14 answerList.map(function(i,answer){

15 var images=$(answer).find('.zm-item-rich-text img');

16 images.map(function(i,image){

17 photos.push($(image).attr("src"));

18 });

19 });

20 setTimeout(function(){

21 offset+=20;

22 console.log("已成功抓取"+photos.length+"张图片的链接");

23 getIAjaxUrlList(offset);

24 },100);

25 }else{

26 console.log("图片链接全部获取完毕,一共有"+photos.length+"条图片链接");

27 // console.log(photos);

28 return downloadImg(50);

29 }

30 }

31 });

32 }

  在代码中post这条请求https://www.zhihu.com/node/QuestionAnswerListV2,把原请求头和请求参数复制下来,作为我们的请求头和请求参数,superagent的set方法可用来设置请求头,send方法可以用来发送请求参数。我们把请求参数中的offset初始为20,每隔一定时间offset再加20,再重新发送请求,这样就相当于我们每隔一定时间发送了一条ajax请求,获取到最新的20条数据,每获取到了数据,我们再对这些数据进行一定的处理,让它们变成一整段的html,便于后面的提取链接处理。

  

异步并发控制下载图片

再获取完了所有的图片链接之后,即判定response.msg为空时,我们就要对这些图片进行下载了,不可能一条一条下对不对,因为如你所看到的,我们的图片足足有

  没错,2万多,还好nodejs有神奇的单线程异步功能,我们可以同时下载这些图片。但是这个时候问题就来了。听说如果同时发送太多请求,会被网站拦截!所以我们绝对不能同时下载超过 20,000 张图片。这时候就需要控制异步并发的数量。

  这里使用了一个神奇的模块=> async,它不仅可以帮助我们询问难以维护的回调金字塔恶魔,还可以轻松帮助我们管理异步进程。具体看文档,这里只使用了一个强大的 async.mapLimit 方法。

<p> 1 var requestAndwrite=function(url,callback){

2 request.get(url).end(function(err,res){

3 if(err){

4 console.log(err);

5 console.log("有一张图片请求失败啦...");

6 }else{

7 var fileName=path.basename(url);

8 fs.writeFile("./img1/"+fileName,res.body,function(err){

9 if(err){

10 console.log(err);

11 console.log("有一张图片写入失败啦...");

12 }else{

13 console.log("图片下载成功啦");

14 callback(null,"successful !");

15 /*callback貌似必须调用,第二个参数将传给下一个回调函数的result,result是一个数组*/

16 }

17 });

18 }

19 });

20 }

21

22 var downloadImg=function(asyncNum){

23 /*有一些图片链接地址不完整没有“http:”头部,帮它们拼接完整*/

24 for(var i=0;i

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线