我有以下代码:
urls
有5000
不同的url,当我试图抓取和刮擦这些urls
时,我遇到了500
错误,所以我决定在每个请求之间添加一些延迟,我添加了{concurrency: 1}
,但没有任何改变。
const requestPromise = require('request-promise');
const Promise = require('bluebird');
const cheerio = require('cheerio');
for (var i=1; i<=250; i++)
{
p="https://mywebsite.com/" + i.toString()
urls[i-1]= p
}
Promise.map(urls, requestPromise)
.map((htmlOnePage, index) => {
const $ = cheerio.load(htmlOnePage);
$('.txtSearch1').each(function () {
var h="";
h=$(this).text()
h= h.replace(/(rn|n|r)/gm, "")
html44.push (h)
})
shareTuple[urls[index]] = html44;
html44=[]
fs.writeFileSync( "data.json", JSON.stringify( shareTuple ) )
}, {concurrency: 1})
.then ()
.catch((e) => console.log('We encountered an error' + e));
我如何在每个请求之间添加一些随机延迟?我应该使用我的代码,所以我需要一个解决方案或修改我的代码。
更新:
我从答案中学习,但这个问题只剩下一点。我如何检测哪个URL导致500个错误并跳过它?我怎么能找到关于URL跑到500错误?
您似乎有一点问题,您传递给什么函数的参数。当前您的操作如下
Promise.map(urls, requestPromise)
.map((htmlOnePage, index) => { ...}, { concurrency: 1})
.then(...)
有多个问题,所以我很想知道如何在不抛出语法错误的情况下运行…
您没有将您的选项
{ concurrency: 1}
传递给Promise.map
,而是传递给后者Array.map
(在那里它们被忽略)Promise.map
返回一个Promise,它没有.map()
Array.map
不返回承诺,所以你不能在它上调用then()
…您正在(同步地)为每个返回值写入非常相同的
data.json
文件。您可能希望先检查结果,然后在完成所有操作后写入文件。
正确的代码应该是这样的
import { promises as fs } from "fs"; //provides promise based fs operations
Promise.map(urls, requestPromise, { concurrency: 1})
.then(values => {
values.map((htmlOnePage, index) => {
const $ = cheerio.load(htmlOnePage);
...
html44.push (h)
})
let sharetuple = html44;
return fs.writeFile("data.json", JSON.stringify(sharetuple));
})
.catch((e) => console.log('We encountered an error' + e));
我不知道,是否cheerio
是异步的东西。我想没有。如果是,你必须相应地处理…
编辑
如果你仍然认为,你需要一个延迟,你可以添加如下(但我认为,你应该在后端解决这个问题,如果你可以访问它)
function delayedRequest(url) {
return new Promise(res => setTimeout(res, 100))
.then(() => requestPromise(url));
}
然后调用
Promise.map(urls, delayedRequest, { concurrency: 1})
.then(values => {
values.map((htmlOnePage, index) => {
const $ = cheerio.load(htmlOnePage);
...
html44.push (h)
})
let sharetuple = html44;
return fs.writeFile("data.json", JSON.stringify(sharetuple));
})
.catch((e) => console.log('We encountered an error' + e));
但是你也可以完全抛弃Bluebird,用JS内置的async await
来做
async function scraper(urls) {
for (let u of urls) {
await new Promise(res => setTimeout(res, 100));
let res = await requestPromise(url);
...
html44.push(h)
}
await fs.writeFile("data.json", JSON.stringify(html44));
}
您的第二个.map
调用所做的是等待,直到所有请求都被解决,这是并行发送的,然后与html processing.callback进行另一轮映射
虽然我认为derpirscher的建议应该可行,但我在这里给出我的建议
Promise.map(
urls,
(url, index) => {
return requestPromise(url).then((htmlOnePage) => {
const $ = cheerio.load(htmlOnePage);
const html44 = [];
$(".txtSearch1").each(function () {
var h = "";
h = $(this).text();
h = h.replace(/(rn|n|r)/gm, "");
html44.push(h);
});
shareTuple = html44;
fs.writeFileSync("data.json", JSON.stringify(shareTuple));
// delay 5s
return new Promise((resolve) => setTimeout(resolve, 5e3));
});
},
{
concurrency: 1,
}
).catch((e) => console.log("We encountered an error" + e));