使用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```
楼主是想要一个队列并且需要它能并发是吧,那为何不考虑直接使用async本身就已经实现的队列queue
?
push pause resume kill 并发等一应俱全,比mapLimit
更适合来控制爬虫
回应你的需求:
1.不让爬虫重复爬取url
queue
是队列,每个任务只会执行一次,可并发但不会重复
2.如何判断爬虫已经爬完了所有的url?
queue
有一个drain
的事件,即队列为空时触发,也就是所有的url爬完了
https://github.com/caolan/async#queue
@zhang2333 感谢回复和建议 上面代码是参考@alsotang 的nodelesson教程 比葫芦画瓢写的代码去完成一个简单的爬虫,还有很多需要完善的,正在看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
我说少个=
是问你是不是把条件判断错写成了赋值,你遇到的问题当然不会是这个原因…
解决了就好,周末(已经没了)愉快~