Node爬虫之使用 async 控制并发

上文 《Node爬虫之使用 eventproxy 控制并发》我们说到用 node 爬了csdn首页数据,但是这些请求完全是并发的,如果某些网站有 “反爬” 机制,就很有可能封锁你的 IP。这样的情况下我们就可以使用 async 模块。主要用到的是 async 的 mapLimit(arr, limit, iterator, callback)接口。

async.mapLimit(arr, 5, function (url, callback) {
  fetchUrl(url, callback);
}, function (err, result) {
  res.send(result);
});

第一个参数 arr为数组,保存了需要爬取页面的 url,第二个参数 limit 表示并发爬取数量。第三个参数是迭代函数(每个 url 需要执行这个函数),其第一个参数 url,是 urls 数组的每个 item,第二个参数 callback 与 mapLimit 方法第四个参数有关,callback 会往 result 参数里存放数据。如何理解?callback 是第三个参数 iterator 的回调,以爬虫为例,爬完页面肯定会分析一些数据,然后保存,执行 callback 函数就能把结果保存在 result(第四个参数函数中的参数) 中。

首先,我们采集 需要爬取页面的 url,保存在 urls 数组中:

$('.clearfix .list_con  .title h2 a').each(function(idx, element) {
	 var $element = $(element);
	 // console.log('$element',$element);

	 var href = url.resolve(csdn, $element.attr('href'));
	 urls.push(href);
});

接着我们用 async 进行异步爬取:

async.mapLimit(urls, 5, function(url, callback) {
	fetchUrl(url, callback);
    },function(err, result) {
	console.log('final: ', result);
	response.send(result);
})

通过fetchUrl函数来抓起页面内容:

var fetchUrl = function(url, callback) {
				superagent.get(url)
					.end(function(err, res) {
						if(err) {
							console.log(err);
						}
						console.log('fetch ' + url + ' successful!');
						var $ = cheerio.load(res.text);
						var jsonData = {
							title: $('.title-article').text().trim(),
							href: url,
							comment1: $('.comment').eq(0).text().trim() 
						};
						console.log(JSON.stringify(jsonData));
						callback(null, jsonData);

					})
			}

这里要注意的是 callback 中的第二个参数,其实 jsonData 已经储存在了 mapLimit 方法第四个参数的 result 参数中。

完整代码:

var express = require('express');
var superagent = require('superagent');
var cheerio = require('cheerio');
var async = require('async');
var url = require('url');

var csdn = 'https://blog.csdn.net/';
var app = express();

app.get('/', function(request, response, next) {
	superagent.get(csdn)
		.end(function(err, res) {
			if(err) {
				return console.error(err);
			}

			var urls = [];
			var $ = cheerio.load(res.text);

			$('.clearfix .list_con  .title h2 a').each(function(idx, element) {
				var $element = $(element);
				// console.log('$element',$element);

				var href = url.resolve(csdn, $element.attr('href'));
				urls.push(href);
			});

			console.log('urls', urls);

			var concurrencyCount = 0;
			var fetchUrl = function(url, callback) {


				superagent.get(url)
					.end(function(err, res) {
						if(err) {
							console.log(err);
						}
						console.log('fetch ' + url + ' successful!');

						var $ = cheerio.load(res.text);
						var jsonData = {
							title: $('.title-article').text().trim(),
							href: url,
							comment1: $('.comment').eq(0).text().trim() 
						};

						console.log(JSON.stringify(jsonData));
						callback(null, jsonData);

					})
			}

			async.mapLimit(urls, 5, function(url, callback) {
				fetchUrl(url, callback);
			},function(err, result) {
				console.log('final: ', result);
				response.send(result);
			})
		})

	
})

app.listen(3002, function() {
	console.log('app is listenling at port 3002');
})

结果:

在这里插入图片描述

还有个问题是,什么时候用 eventproxy,什么时候使用 async 呢?它们不都是用来做异步流程控制的吗?

当你需要去多个源(一般是小于 10 个)汇总数据的时候,用 eventproxy方便;当你需要用到队列,需要控制并发数,或者你喜欢函数式编程思维时,使用 async。大部分场景是前者。
参考:使用 async 控制并发

猜你喜欢

转载自blog.csdn.net/qq_32682137/article/details/82769934