1

我在使用 Node.js 运行的程序中有一个 for 循环。该函数是xray包中的 x() ,我使用它从网页抓取和接收数据,然后将该数据写入文件。该程序在用于刮〜100页时成功,但我需要刮〜10000页。当我尝试抓取大量页面时,会创建文件但它们不包含任何数据。我相信这个问题的存在是因为 for 循环在继续下一次迭代之前没有等待 x() 返回数据。

有没有办法让节点在继续下一次迭代之前等待 x() 函数完成?

//takes in file of urls, 1 on each line, and splits them into an array. 
//Then scrapes webpages and writes content to a file named for the pmid number that represents the study
 
//split urls into arrays
var fs = require('fs');
var array = fs.readFileSync('Desktop/formatted_urls.txt').toString().split("\n");


var Xray = require('x-ray');
var x = new Xray();
 
for(i in array){
        //get unique number and url from the array to be put into the text file name
                number = array[i].substring(35);
                url = array[i];


        //use .write function of x from xray to write the info to a file
        x(url, 'css selectors').write('filepath' + number + '.txt');
                               
}

注意:我正在抓取的某些页面不返回任何值

4

2 回答 2

2

您不能让for循环等待异步操作完成。要解决此类问题,您必须进行手动迭代,并且需要挂钩到异步操作的完成函数。以下是其工作原理的大致轮廓:

var index = 0;
function next() {
    if (index < array.length) {
        x(url, ....)(function(err, data) {
            ++index;
            next();
        });
    }
}
next();

或者,也许这个;

var index = 0;
function next() {
    if (index < array.length) {
        var url = array[index];
        var number = array[i].substring(35);
        x(url, 'css selectors').write('filepath' + number + '.txt').on('end', function() {
            ++index;
            next() 
        });
    }
}
next();
于 2015-11-17T03:33:13.687 回答
2

您的代码的问题在于您没有等待将文件写入文件系统。比逐个下载文件更好的方法是一次性完成它们然后等待它们完成,而不是在继续下一个之前逐个处理它们。

在 nodejs 中处理 Promise 的推荐库之一是 bluebird。

http://bluebirdjs.com/docs/getting-started.html

在更新后的示例中(见下文),我们遍历所有 url 并开始下载,并跟踪承诺,然后一旦文件被写入,每个承诺都会得到解决。最后,我们只需等待所有的 Promise 使用 Promise.all() 得到解决

这是更新的代码:

var promises = [];
var getDownloadPromise = function(url, number){
    return new Promise(function(resolve){
        x(url, 'css selectors').write('filepath' + number + '.txt').on('finish', function(){
            console.log('Completed ' + url);
            resolve();
        });
    });
};

for(i in array){
    number = array[i].substring(35);
    url = array[i];

    promises.push(getDownloadPromise(url, number));                               
}

Promise.all(promises).then(function(){
    console.log('All urls have been completed');
});
于 2015-11-17T06:17:17.703 回答