【问题标题】:Best Practice for retrying page.goto, page.waitForNavigation etc. in puppeteer/JavaScript在 puppeteer/JavaScript 中重试 page.goto、page.waitForNavigation 等的最佳实践
【发布时间】:2019-11-14 23:30:15
【问题描述】:

我正在尝试使用 Puppeteer 和 tor 包 (apt install tor) 在 TOR 网络中抓取一些网页。 可能是由于 TOR 连接的性质,有时我会超时。 另外,我是 JavaScript 异步编程的新手。

通常我有一个这样的 try-catch-construct:

await Promise.all([
  page.goto(url),
  page.waitForNavigation({
    waitUntil: 'domcontentloaded'
  }),
]).catch((err) => { logMyErrors(err, true); });

let langMenu = await page.waitForXPath('//*[contains(@class, ".customer_name")]/ancestor::li').catch((err) => { logMyErrors(err, true); });

但我认为通常一次或多次重试将有助于最终获得所需的资源。是否有实施重试的最佳实践?

【问题讨论】:

  • 这取决于您的要求。可能是增加超时和/或在取消之前设置最大重试次数,然后在超时时重复请求(?)。所以没有法律,你必须根据你的目标/系统/资源等来决定。
  • 是的,但是在多次重试的情况下,我不确定如何在不产生大量嵌套代码的情况下实现重试。还是太简单了?您将如何重试上述let langMenuexample 3 次?
  • 我添加了一个答案,它有效吗?
  • 我尝试将它集成到我的代码中,但未能成功。但这更可能是由于我对异步 JavaScript 编程的知识还不够。我能够整合 Thomas Dondorf 的更简洁的建议。

标签: javascript node.js async-await puppeteer tor


【解决方案1】:

这是一个没有厄运金字塔的重试示例。 虽然我不是 ES 专家,但可能会有一些新的 async/await 功能可以使代码更干净,但现在可以:

function retry(callback, retries) {
    let tries = 0;

    function tryRequest() {
        tries++;
        return callback().catch(e => {
            logMyErrors(e);

            if (tries < retries) {
                return tryRequest();
            }
        });
    }

    return tryRequest();
}

const logMyErrors = console.log;


retry(() => {
    console.log("retry");
    return new Promise((resolve, reject) => {
        //Emulate some rejections here
        if (Math.random() > 0.2) {
            throw new Error("Something went wrong."); 
        }

        resolve("Success");
    });
}, 10).then((result) => {}, (rejected) => {});

函数retry 接受一个必须返回一个承诺的回调。 callback 一直执行,直到 Promise 完成(解决、拒绝)或达到 retries 的数量。

【讨论】:

    【解决方案2】:

    我会推荐这种相当简单的方法:

    async function retry(promiseFactory, retryCount) {
      try {
        return await promiseFactory();
      } catch (error) {
        if (retryCount <= 0) {
          throw error;
        }
        return await retry(promiseFactory, retryCount - 1);
      }
    }
    

    此函数调用promiseFactory,并等待返回的 Promise 完成。如果发生错误,该过程会(递归地)重复,直到retryCount 到达0

    代码示例

    你可以这样使用函数:

    await retry(
      () => page.waitForXPath('//*[contains(@class, ".customer_name")]/ancestor::li'),
      5 // retry this 5 times
    );
    

    您还可以传递任何其他返回 Promise 的函数,例如 Promise.all

    await retry(
      () => Promise.all([
        page.goto(url),
        page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
      ]),
      1 // retry only once
    );
    

    不要将 await 和 catch 结合起来

    另一个建议:您不应该将await.then.catch 结合使用,因为这会导致意外问题。要么使用await 并用try..catch 块包围你的代码,要么使用.then.catch。否则,您的代码可能正在等待 catch 函数的结果完成,等等。

    相反,您可以像这样使用try..catch

    try {
      // ...
    } catch (error) {
      logMyErrors(error);
    }
    

    【讨论】:

      猜你喜欢
      • 2020-06-12
      • 1970-01-01
      • 2012-12-06
      • 1970-01-01
      • 1970-01-01
      • 2020-11-20
      • 2013-11-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多