async.mapLimit配合superagent 使用时候结果重复该如何控制输出?
发布于 8 年前 作者 herohenu 4754 次浏览 来自 问答

使用superagent 爬网站的时候使用async 对并发个数进行控制,并发个数确保了,但是数据的结果重复了:爬取的数组长度是10,最后结果输出了10次,求教该如何控制输出结果不重复(或者说如何判断全部url爬完)? 代码如下:

var eventproxy = require('eventproxy');
var superagent = require('superagent');
var cheerio = require('cheerio');
var url = require('url');
var async = require('async');
var fs = require('fs');

var itjuziUrl = 'http://www.itjuzi.com/company/';
var companyUrls = [];
for (i = 1; i <= 10; i++) {
  var href = url.resolve(itjuziUrl, i.toString());  //
  companyUrls.push(href);
}

var companyJson = [];
var concurrencyCount = 0;
var fetchUrl= function(url ,callback){
  companyurl = url;
  concurrencyCount ++ ;
  superagent.get(companyurl)
    .end(function (err, res) {
      if(err){
          callback(err, 'fetch url:' + companyurl + ' err: ');
        return console.log('fetch url:' + companyurl + ' err: ', err);
      }
      console.log('现在并发数目是******: ',concurrencyCount,' **** fetch ' + companyurl + ' successful');
      //ep.emit('topic_html', [companyurl, res.text]);
      var $ = cheerio.load(res.text);
      concurrencyCount --;
      companyJson.push({
        productName: $('div>span.title').text().replace(/[\n\t\r\s]/g,""),
        companyName:  $('div.des-more>div> span').eq(0).text().replace(/[\n\t\r\s]/g,""),
        address: $('span.loca.c-gray-aset').text().replace(/[\n\t\r\s]/g,""),
        stage: $('td.mobile-none').text().replace(/[\n\t\r\s]/g,""),
        mainpage: $('a.weblink').attr('href'),
        hirepage: ''
      });
       callback(null, companyJson);
    });
}

async.mapLimit(companyUrls , 5, function(url,callback){
  fetchUrl(url ,callback)
},function(err, result ){
  //console.log('now  err , result  is ',result);
  if (!err) {
    console.log('result.length  ',result.length );
    if (companyUrls.length = result.length) {
      arr = result;
      var file = fs.createWriteStream('result.json');
      file.on('error', function (err) {
        console.log('write  file  error ', err);
      });
      arr.forEach(function (v) {
        //console.log('v is  ', JSON.stringify(v));
        file.write(JSON.stringify(v) + '\n');
      });
      file.end();
    }
  }
})
\n```
6 回复

楼主是想要一个队列并且需要它能并发是吧,那为何不考虑直接使用async本身就已经实现的队列queue? push pause resume kill 并发等一应俱全,比mapLimit更适合来控制爬虫 回应你的需求: 1.不让爬虫重复爬取url queue是队列,每个任务只会执行一次,可并发但不会重复 2.如何判断爬虫已经爬完了所有的url? queue有一个drain的事件,即队列为空时触发,也就是所有的url爬完了 https://github.com/caolan/async#queue

@zhang2333 感谢回复和建议 上面代码是参考@alsotangnodelesson教程 比葫芦画瓢写的代码去完成一个简单的爬虫,还有很多需要完善的,正在看async的文档,对爬虫进行改进,不过还是想知道是代码哪里的问题导致结果会重复10次写入到文件中。

@herohenu 1.fetchUrl中的callback(null, companyJson);你这里返回的是数组,而最终mapLimit的回调得到的就是数组的数组(即[companyJson,companyJson,companyJson....]),不知道你所说的结果重复是不是指的这个 2.mapLimit的第三个参数直接写fetchUrl就可以了,没必要再写个匿名函数包裹它 3.if (companyUrls.length = result.length) {是不是少个= 4.至于为什么结果输出了10遍,你指的是console.log('result.length ',result.length );输出了10遍吗

研究了半天,还是自己解决了 。 去掉这个 companyJson.push(),把组装的obj对象 传递给callback就可以了

@zhang2333 谢谢指正哈,正如你所言,callback(null, companyJson) 这个地方修改为callback(null, obj);就可以了。 之前的结果重复同你说的1 一样,返回的是数组的数组。 那个if判断确实是无效的,即使改为== 返回的仍会是数组的数组。 刚开始学习async,对这个callback不大熟悉引起的问题。 经过你的指正加上刚才调试,又有了新的收获,非常感谢,周末愉快!

@herohenu 我说少个=是问你是不是把条件判断错写成了赋值,你遇到的问题当然不会是这个原因… 解决了就好,周末(已经没了)愉快~

回到顶部