0

这是一个用 node js 编写的网页抓取代码。
当队列有足够的 url 时,此代码是否会始终保持 5 个并发请求?
为什么控制台显示其他内容?

var request = require("request");
var cheerio = require("cheerio");
var fs = require('fs');

var concurrent_requests = 0;
var queue = [];
var baseUrl = "https://angularjs.org/";

function makeApiCall(url){
    if(url) {
        queue.unshift(url);
    }
    if(concurrent_requests<5) {
        var nextUrl = queue.pop();
        if(nextUrl) {
            concurrent_requests++;
            request(nextUrl, function (error, response, body) {
                var invalidUrl;
                concurrent_requests--;
                if(body) {
                    var $ = cheerio.load(body);
                    var anchors = $("a");
                    var data = "";
                    for (var i = 0; i < anchors.length; i++) {
                        url = $(anchors[i]).attr("href");
                        if(!url || url === "#" || url === "javascript:void(0)"){
                            invalidUrl = true;
                        }
                        else{
                             invalidUrl = false;
                        }

                        if (!invalidUrl) {
                            makeApiCall(url);
                            data += url + ", " + nextUrl + "\n";
                        }
                    }
                    //console.log(data);
                    fs.appendFile('urls.csv',data, function (err) {
                        if (err) throw err;
                    });
                }
                else{
                    makeApiCall();
                }
            });
        }
    }
     console.log(concurrent_requests);

}


makeApiCall(baseUrl);
4

2 回答 2

1

Becoz,您有条件声明不要使用 if 语句请求超过 5 个。

如果(并发请求<5){

该解决方案不可扩展,因为在某些递归调用后会遍历堆栈。

希望能帮助到你。

于 2017-10-07T19:30:58.393 回答
1

您正在使用 if 条件来检查并发请求的数量是否少于五个。但请记住,它是if语句,而不是循环。这意味着它只会被调用一次。

您正在makeApiCall请求的回调中对您的函数进行递归调用。请求的回调仅在请求完成时运行。

考虑到以上两点,在您的 if 条件下,您检查是否concurrent_requests<5调用 request 方法,并且您的程序很理想。在请求 id 满足的某个时间后,请求的回调运行,在一些逻辑之后makeApiCall再次调用。因此,在每次通话中,您只调用一次请求,然后等待它解决,然后只有您的程序继续进行下一个请求。

如果您想要并发请求,请使用这样的循环

function makeApiCall(url){
    if(url) {
        queue.unshift(url);
    }
    // Use a loop here
    while(concurrent_requests<5) {
        var nextUrl = queue.pop();
        if(nextUrl) {
            concurrent_requests++;
            request(nextUrl, function (error, response, body) {
                var invalidUrl;
                concurrent_requests--;
                if(body) {
                        ...
                        if (!invalidUrl) {
                            makeApiCall(url);
                            data += url + ", " + nextUrl + "\n";
                        }
                    }
                    ...
                }
                else{
                    makeApiCall();
                }
            });
        }
        else{
           // Remember to break out of loop when queue is empty to avoid infinite loop.
           break;
        }
    }
     console.log(concurrent_requests);

}
于 2017-10-08T06:32:07.843 回答