【问题标题】:Nodejs: Async request with a list of URLNodejs:带有URL列表的异步请求
【发布时间】:2017-11-15 04:03:22
【问题描述】:

我正在开发一个爬虫。我有一个需要请求的 URL 列表。如果我不将其设置为异步,则同时有数百个请求。我担心它会爆炸我的带宽或产生对目标网站的大量网络访问。我该怎么办?

这是我正在做的事情:

urlList.forEach((url, index) => {

    console.log('Fetching ' + url);
    request(url, function(error, response, body) {
        //do sth for body

    });
});

我想在一个请求完成后调用一个请求。

【问题讨论】:

    标签: node.js request web-crawler


    【解决方案1】:

    您可以使用诸如 Promise 库之类的东西,例如sn-p

    const Promise = require("bluebird");
    const axios = require("axios");
    
    //Axios wrapper for error handling
    const axios_wrapper = (options) => {
        return axios(...options)
            .then((r) => {
                return Promise.resolve({
                    data: r.data,
                    error: null,
                });
            })
            .catch((e) => {
                return Promise.resolve({
                    data: null,
                    error: e.response ? e.response.data : e,
                });
            });
    };
    
    Promise.map(
        urls,
        (k) => {
            return axios_wrapper({
                method: "GET",
                url: k,
            });
        },
        { concurrency: 1 } // Here 1 represents how many requests you want to run in parallel
    )
        .then((r) => {
            console.log(r);
            //Here r will be an array of objects like {data: [{}], error: null}, where if the request was successfull it will have data value present otherwise error value will be non-null
        })
        .catch((e) => {
            console.error(e);
        });
    

    【讨论】:

      【解决方案2】:

      您需要注意的是:

      1. 目标站点是否有速率限制,如果您尝试请求太多太快可能会被阻止访问?

      2. 目标站点可以同时处理多少个请求而不降低其性能?

      3. 您的服务器有多少带宽?

      4. 您自己的服务器可以同时处理和处理多少个请求,而不会导致过多的内存使用或固定 CPU。

      一般来说,管理所有这些的方案是创建一种方法来调整您启动的请求数量。有许多不同的方法可以通过同时请求的数量、每秒的请求数、使用的数据量等来控制这一点......

      最简单的开始方法是控制您同时发出的请求数。可以这样做:

      function runRequests(arrayOfData, maxInFlight, fn) {
          return new Promise((resolve, reject) => {
              let index = 0;
              let inFlight = 0;
      
              function next() {
                  while (inFlight < maxInFlight && index < arrayOfData.length) {
                      ++inFlight;
                      fn(arrayOfData[index++]).then(result => {
                          --inFlight;
                          next();
                      }).catch(err => {
                          --inFlight;
                          console.log(err);
                          // purposely eat the error and let the rest of the processing continue
                          // if you want to stop further processing, you can call reject() here
                          next();
                      });
                  }
                  if (inFlight === 0) {
                      // all done
                      resolve();
                  }
              }
              next();
          });
      }
      

      然后,你会像这样使用它:

      const rp = require('request-promise');
      
      // run the whole urlList, no more than 10 at a time
      runRequests(urlList, 10, function(url) {
          return rp(url).then(function(data) {
              // process fetched data here for one url
          }).catch(function(err) {
              console.log(url, err);
          });
      }).then(function() {
          // all requests done here
      });
      

      可以通过向其添加时间元素(每秒不超过 N 个请求)甚至带宽元素来使其变得任意复杂。

      我想在一个请求完成后调用一个请求。

      这是一种非常缓慢的做事方式。如果你真的想要,那么你可以将1maxInFlight 参数传递给上述函数,但通常情况下,通过允许5 到50 个同时请求,事情会运行得更快并且不会引起问题。只有测试才能告诉您特定目标站点和特定服务器基础架构的最佳位置以及您需要对结果进行的处理量。

      【讨论】:

        【解决方案3】:

        您可以使用设置超时功能来处理循环内的所有请求。为此,您必须知道处理请求的最长时间。

        【讨论】:

          猜你喜欢
          • 2013-10-23
          • 1970-01-01
          • 2012-02-25
          • 2016-09-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-12-09
          相关资源
          最近更新 更多