使用 Cheerio 和 NodeJS 在 Promis.map 中的请求之间添加一些延迟?

add some delays between request in promis.map using cheerio and nodejs?

提问人: 提问时间:7/16/2022 更新时间:7/18/2022 访问量:253

问:

我有以下代码:

urls有不同的 url,当我尝试抓取和抓取这些时,我遇到了一个错误,所以我决定在每个请求之间添加一些延迟,我添加了但没有改变。5000urls500{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(/(\r\n|\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 错误的信息?

javascript node.js web-scraping bluebird cheerio

评论

1赞 Alex Brohshtut 7/16/2022
500 错误不是必需的,这意味着您需要限制您的请求。429 太多请求意味着。所以也许调查一下为什么你一开始就得到 500 分?
0赞 Alex Brohshtut 7/16/2022
你能提供一段代码吗?另外,你正在使用对吗?Bluebird
0赞 derpirscher 7/16/2022
什么?不是标准的JS,是吗?Promise.map
0赞 derpirscher 7/16/2022
@AlexBrohshtut 由于我们不知道调用了什么 API,它可能只是实现得很糟糕,无法以正确的方式处理许多请求,只会抛出异常......
0赞 Alex Brohshtut 7/16/2022
@derpirscher对。另外,@moji-moji,看来你根本没有在等待承诺内的行动......所以基本上你是在逐个发射所有这些,但虽然没有等待完成,但就像根本没有财产一样。concurrecy

答:

1赞 derpirscher 7/16/2022 #1

您似乎对将哪些参数传递给哪个函数有点问题。目前,您可以执行以下操作

Promise.map(urls, requestPromise)
  .map((htmlOnePage, index) => { ...}, { concurrency: 1})
  .then(...)

它有多个问题,所以我很想知道它如何在不抛出语法错误的情况下运行......

  1. 你不是把你的选择传递给后者,而是传递给后者(它们被忽略){ concurrency: 1}Promise.mapArray.map

  2. Promise.map返回一个 Promise,它没有.map()

  3. Array.map不返回承诺,所以你不能调用它......then()

  4. 您正在(同步)将每个返回值写入同一个文件。您可能希望先浏览结果,然后在所有操作完成后编写文件。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));
}
0赞 edvard chen 7/16/2022 #2

第二次调用的作用是等到所有请求都解析完毕,并行发送,然后使用 html processing.callback 进行另一轮映射.map

虽然我认为 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(/(\r\n|\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));

评论

0赞 edvard chen 7/17/2022
在我的回答中更新
0赞 edvard chen 7/17/2022
免责声明:我的代码是演示正确的方向,而不是提供可以直接使用的工作版本。你想要的应该从回调中获取index.map
0赞 7/17/2022
所以非常好。知道了。一个问题仍然存在。我怎么能说如果一个 url 达到错误 500,请转到下一个 url 地址?绕过 500 并获取下一个 URL?这完全解决了我的问题。
0赞 edvard chen 7/17/2022
只需抓住可能拒绝requestPromise并吞下它。requestPromise(...).then(...).catch(e=> do_wharever_you_want_just_not_to_throw_error)