一般我們在寫爬蟲的時候,很多網站會因為你並發請求數太多當做是在惡意請求,封掉你的IP,為了防止這種情況的發生,我們一般會在代碼里控制並發請求數,Node里面一般借助async模塊來實現。
1. async.mapLimit方法
mapLimit(arr, limit, iterator, callback)
arr中一般是多個請求的url,limit為並發限制次數,mapLimit方法將arr中的每一項依次拿給iterator去執行,執行結果傳給最后的callback;
2. async.mapLimit方法應用
下面是之前寫過的一個簡單的爬蟲示例,將爬取到的新聞標題和路徑保存在一個Excel表格中,限制並發數為3,代碼如下
webSpider.js:
//request調用url主函數 (mapLimit iterator)
function main(option, callback) {
n++;
timeline[option] = new Date().getTime();
console.log('現在的並發數是', n, ',正在抓取的是', option);
request(option, function(err, res, body) {
if(!err && res.statusCode == 200){
var $ = cheerio.load(body);
$('#post_list .post_item').each(function(index, element) {
// console.log(element);
var item = [$(element).find('.post_item_body h3 a').text(),$(element).find('.post_item_body h3 a').attr('href')];
dataArr[0].data.push(item);
});
console.log('抓取', option, '結束,耗時:', new Date().getTime()-timeline[option], '毫秒');
n--;
callback(null, 'done!');
}else{
console.log(err);
n--;
callback(err, null);
}
});
}
//限制請求並發數為3
async.mapLimit(options, 3, main.bind(this), function(err, result){
if(err) {
console.log(err);
} else {
fs.writeFile('data/cnbNews.xlsx', xlsx.build(dataArr), 'utf-8', function(err){
if(err){
console.log('write file error!');
}else{
console.log('write file success!');
}
});
}
});
這里迭代器里面第二個參數callback(即請求每一條url完成之后的回調方法)是關鍵,沒有異常的情況下所有options中的url都請求完成之后會回調mapLimit方法的回調方法進行后續操作(如這里的生成文件),如果單條url請求異常,回調方法中會接收到err並報出錯誤,不能執行后續生成文件的操作。
async.mapLimit(options, 3, function(option, callback) {
request(option, main);
callback(null);
}, function(err, result) {
if(err) {
console.log(err);
} else {
console.log('done!');
}
});
如上,網上有些資料中是在迭代器中request方法執行完成之后調用callback,因為request方法異步接收請求數據,這種寫法會使async.mapLimit方法limit參數無效,導致無法達到限制請求並發數的目的,這里需要注意下。
執行webSpider.js,
node webSpider.js

3. 總結
執行結果可以看到並發數依次增加,增加到3時不再繼續增加,等待前面一條請求執行完成后才會請求下一條,這樣的話,如果我們需要爬取1000條數據,就可以並發10條請求,慢慢爬完這1000個鏈接,這樣就不用擔心因並發太多被封IP這種情況發生了。完整代碼已上傳GitHub,有興趣去試試吧!
