async和enterproxy控制并发数量

聊聊并发与并行

并发咱们常常说起之,不论是web server,app并发无处不在,操做系统中,指一个时间段中几个程序处于已经启动运行到完毕之间,且这几个程序都是在同一处理机上运行,而且任一个时间点只有一个程序在处理机上运行。不少网站都有并发链接数量的限制,因此当请求发送太快的时候会致使返回值为空或报错。更有甚者,有些网站可能由于你发出的并发链接数量过多而当你是在恶意请求,封掉你的ip。html

相对于并发,并行可能陌生了很多,并行指一组程序按独立异步的速度执行,不等于时间上的重叠(同一个时刻发生),经过增长cpu核心来实现多个程序(任务)的同时进行。没错,并行作到了多任务的同时进行node

使用enterproxy控制并发数量

enterproxy是朴灵大大为主要贡献的工具,带来一种事件式编程的思惟变化,利用事件机制解耦复杂业务逻辑,解决了回调函数耦合性的诟病,将串行等待变成并行等待,提高多异步协做场景下的执行效率git

咱们如何使用enterproxy控制并发数量?一般若是咱们不使用enterproxy和自制的计数器,咱们若是抓取三个源:github

这种深层嵌套,串行的方式web

var render = function (template, data) {
  _.template(template, data);
};
$.get("template", function (template) {
  // something
  $.get("data", function (data) {
    // something
    $.get("l10n", function (l10n) {
      // something
      render(template, data, l10n);
    });
  });
});

除去这种过去深层嵌套的方法,咱们常规的写法的本身维护一个计数器npm

(function(){
    var count = 0;
    var result  = {};
    
    $.get('template',function(data){
        result.data1 = data;
        count++;
        handle();
    })
    $.get('data',function(data){
        result.data2 = data;
        count++;
        handle();
    })
    $.get('l10n',function(data){
        result.data3 = data;
        count++;
        handle();
    })

    function handle(){
        if(count === 3){
            var html = fuck(result.data1,result.data2,result.data3);
            render(html);
        }
    }
})();

在这里,enterproxy就能够起到这个计数器的做用,它帮你管理这些异步操做是否完成,完成以后,他会自动调用你提供的处理函数,并将抓取到数据当作参数传递过来编程

var ep = new enterproxy();
ep.all('data_event1','data_event2','data_event3',function(data1,data2,data3){
    var html = fuck(data1,data2,data3);
    render(html);
})

$.get('http:example1',function(data){
    ep.emit('data_event1',data);
})

$.get('http:example2',function(data){
    ep.emit('data_event2',data);
})

$.get('http:example3',function(data){
    ep.emit('data_event3',data);
})

enterproxy还提供了其余很多场景所需的API,能够自行学习下这个API enterproxyjson

使用async控制并发数量

假如咱们有40个请求须要发出,不少网站可能会由于你发出的并发链接数太多而当你是在恶意请求,把你的IP封掉。
因此咱们老是须要控制并发数量,而后慢慢抓取完这40个连接。数组

使用async中mapLimit控制一次性并发数量为5,一次性只抓取5个连接。服务器

async.mapLimit(arr, 5, function (url, callback) {
      // something
    }, function (error, result) {
      console.log("result: ")
      console.log(result);
    })

咱们首先应该知道什么是并发,为何须要限制并发数量,都有哪些处理方案。而后就能够去文档具体看一下API如何使用。async文档能够很好的学习这些语法。

模拟一组数据,这里返回的数据是假的,返回的延时是随机的。

var concurreyCount = 0;
var fetchUrl = function(url,callback){
    // delay 的值在 2000 之内,是个随机的整数 模拟延时
    var delay =  parseInt((Math.random()* 10000000) % 2000,10);
    concurreyCount++;
    console.log('如今并发数是 ' , concurreyCount , ' 正在抓取的是' , url , ' 耗时' + delay + '毫秒');
    setTimeout(function(){
        concurreyCount--;
        callback(null,url + ' html content');
    },delay);
}

var urls = [];
for(var i = 0;i<30;i++){
    urls.push('http://datasource_' + i)
}

而后咱们使用async.mapLimit来并发抓取,并获取结果。

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

模拟摘自alsotang

运行输出后获得如下结果

咱们发现,并发数从1开始增加,可是增加到5时,就不在增长。然有任务时就继续抓取,并发链接数量始终控制在5个。

完成node简易爬虫系统

由于alsotang前辈的《node包教不包会》教程例子中使用的eventproxy控制的并发数量,咱们就来完成一个使用async控制并发数量的node简易爬虫。

爬取的目标就是本站首页(手动护脸)

第一步,首先咱们须要用到如下的模块:

  • url : 用于url解析,这里用到url.resolve()生成一个合法的域名
  • async : 一个实用的模块,提供了强大的功能和异步JavaScript工做
  • cheerio : 为服务器特别定制的,快速,灵活,实施的jQuery核心实现
  • superagent : nodejs里一个很是方便的客户端请求代理模块

    经过npm安装依赖模块

第二步,经过require引入依赖模块,肯定爬取对象URL:

var url = require("url");
var async = require("async");
var cheerio = require("cheerio");
var superagent = require("superagent");

var baseUrl = 'http://www.chenqaq.com';

第三步:使用superagent请求目标URL,并使用cheerio处理baseUrl获得目标内容url,并保存在数组arr中

superagent.get(baseUrl)
  .end(function (err, res) {
    if (err) {
      return console.error(err);
    }
    var arr = [];
    var $ = cheerio.load(res.text);
    // 下面和jQuery操做是同样同样的..
    $(".post-list .post-title-link").each(function (idx, element) {
      $element = $(element);
      var _url = url.resolve(baseUrl, $element.attr("href"));
      arr.push(_url);
    });

    // 验证获得的全部文章连接集合
    output(arr);
    // 第四步:接下来遍历arr,解析每个页面须要的信息

})

咱们须要一个函数验证抓取的url对象,很简单咱们只须要一个函数遍历arr并打印出来就能够:

function output(arr){
    for(var i = 0;i<arr.length;i++){
        console.log(arr[i]);
    }
}

第四步:咱们须要遍历获得的URL对象,解析每个页面须要的信息。

这里就须要用到async控制并发数量,若是你上一步获取了一个庞大的arr数组,有多个url须要请求,若是同时发出多个请求,一些网站就可能会把你的行为当作恶意请求而封掉你的ip

async.mapLimit(arr,3,function(url,callback){
    superagent.get(url)
        .end(function(err,mes){
            if(err){
                console.error(err);
                console.log('message info ' + JSON.stringify(mes));
            }
            console.log('「fetch」' + url + ' successful!');
            var $ = cheerio.load(mes.text);
            var jsonData = {
                title:$('.post-card-title').text().trim(),
                href: url,
            };
            callback(null,jsonData);
        },function(error,results){
            console.log('results ');
            console.log(results);
        })
    })

获得上一步保存url地址的数组arr,限制最大并发数量为3,而后用一个回调函数处理 「该回调函数比较特殊,在iteratee方法中必定要调用该回调函数,有三种方式」

  • callback(null) 调用成功
  • callback(null,data) 调用成功,而且返回数据data追加到results
  • callback(data) 调用失败,不会再继续循环,直接到最后的callback

好了,到这里咱们的node简易的小爬虫就完成了,来看看效果吧

嗨呀,首页数据好少,可是成功了呢。

参考资料

Node.js 包教不包会 - alsotang

enterproxy

async

async Documentation

相关文章
相关标签/搜索