我正在为特定网站构建数据抓取工具。因为我只想每10秒发出一次请求,所以我将它设置为一个setTimeout循环,它将url作为我手动输入的url数组中的参数。在回调中,它请求此url并解析响应,将数据推送到一个新的数组结构中,以便最终可以将其转换为csv。我将粘贴下面的完整代码以及我的依赖项。
问题是这些请求中约有五分之一以未定义的形式返回。我认为超时功能会处理这个并且程序会同步运行我显然是错的。研究这个我发现很多人使用promises依赖来命令异步请求。我的问题是:那是必要的吗?或者我可以调整我的回调/ setTimeout,以便它可以在不添加其他依赖项的情况下工作吗?
编辑由于我不清楚我正在复制我想要这个应用程序做的事情: 我希望程序接受请求,返回一个json字符串,解析该数据的json字符串,将该数据添加到数组,并将该数组导出为csv。我想循环这个功能,所以它可以为一长串URL做这个,但我希望它一次只能发出1个请求,并等待迭代到下一个请求,直到从初始响应中收集到所需的数据。我想每10秒发送一次请求。
这是我的代码:
var express = require('express');
var fs = require('fs');
var request = require('request');
var cheerio = require('cheerio');
var app = express();
var arr = [];
var url = //A bunch of urls that I'm leaving out to conserve space
i = 0;
function timeout() {
setTimeout(function() {
request(url[i], function(error, response, html){
if(error){
console.log(error);
} else {
var $ = cheerio.load(html);
var company, industry, size, website, type;
var inArr = [];
$('div .image-wrapper img').filter(function(){
var data = $(this);
company = data.attr('alt');
inArr.push("\"" + company + "\"");
})
$('.industry p').filter(function(){
var data = $(this);
industry = data.text();
inArr.push("\"" + industry + "\"");
})
$('.company-size p').filter(function(){
var data = $(this);
size = data.text();
inArr.push("\"" + size + "\"");
})
$('.website p a').filter(function(){
var data = $(this);
website = data.text();
inArr.push("\"" + website + "\"");
})
$('.type p').filter(function(){
var data = $(this);
type = data.text();
inArr.push("\"" + type + "\"");
})
arr.push(inArr);
console.log("I just sourced data for " + company);
if (i === url.length - 1) {
clearTimeout(timeout);
console.log("All done!")
var csvContent;
arr.forEach(function(infoArray, index){
dataString = infoArray.join(",");
csvContent += index < arr.length ? dataString+ "\n" : dataString;
});
fs.writeFile('output.csv', csvContent, function(err){
console.log('File successfully written! - Check your project directory for the output.csv file');
});
} else {
i++;
timeout();
}
}
});
}, 10000);
};
timeout();
答案 0 :(得分:1)
如果这里真正的问题是你想要一个接一个地发送一个请求,但是相隔不超过10秒,那么你可以这样做,通过制作一个小包装函数来告诉你下次调用的时间:
var request = require('request');
function requestNext(url, callback, delay, nextCallback) {
var start = Date.now();
request(url, function(error, response, html) {
callback(error, response, html);
var elapsed = Date.now() - start;
var wait = Math.max(delay - elapsed, 0);
// schedule next call to request()
setTimeout(nextCallback, wait);
});
}
然后,你可以调用requestNext()来指定延迟时间,再调用第二个回调来告诉你何时进行下一次调用。
然后,在你特别的情况下,你可以使用这样的重复函数:
function getURLs(urls, delay, processCallback, doneCallback) {
var index = 0;
var data = [];
function next() {
if (index < urls.length) {
requestNext(urls[index++], function(err, response, html) {
// need to decide what you want do for error handling here
// continue? stop further processing?
data.push(processCallback(err, response, html));
}, delay, next);
} else {
doneCallback(null, data);
}
}
next();
}
getURLs(urlArray, 10000, processResult, function(err, dataArray) {
if (!err) {
// results are in dataArray
}
});
然后,将您的逻辑用于处理名为processResult的函数中的URL,如下所示:
function processResult(err, response, html) {
// your code to process a page here
// return the final result as a return value and it will be collected for you
}
答案 1 :(得分:0)
尝试调整js
以声明引用setTimeout
的变量。在问题js
处,clearTimeout()
作为参数调用timeout
,但timeout
是一个函数,而不是对setTimeout
内timeout()
的引用}。 js
以下t
timeout
被声明为setTimeout
函数之外的变量,在timeout()
调用中设置为var timeoutID = window.setTimeout(func, [delay, param1, param2, ...]);
var timeoutID = window.setTimeout(code, [delay]);
的引用。
<强>语法强>
var arr = []; var url = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; var i = 0; var t = null; function timeout() { t = setTimeout(function() { (function() { var inArr = []; inArr.push(i) arr.push(inArr); console.log("I just sourced data for " + i, t); if (i === url.length - 1) { clearTimeout(t); console.log("All done!", i, url.length - 1, arr) } else { i++; timeout(); } }()); }, 1000); // reduced duration to `1000` for stacksnippets }; timeout();
func articles() -> [Article] {
return arrayOfModels.map(ArticleStruct.init)
}
func authors() -> [Author] {
return arrayOfModels.map(AuthorStruct.init)
}
&#13;