在 puppeteer/JavaScript 中重试 page.goto、page.waitForNavigation 等的最佳实践

问题描述 投票:0回答:3

我正在尝试使用 Puppeteer 和 tor 包(

apt install tor
)抓取 TOR 网络中的一些网页。 可能由于 TOR 连接的性质,有时我会超时。 此外,我是 JavaScript 异步编程的新手。

通常我有一个像这样的 try-catch-构造:

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); });

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

javascript node.js async-await puppeteer tor
3个回答
15
投票

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

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);
}

2
投票

工作流程

  • 定义重试函数
  • 如果 Promise 拒绝则运行重试函数
  • 使用 retryCount 控制重试

简洁的复制+粘贴解决方案:

const GotoWithRetry = async (page, url, retryCount = 2) => {
  if (retryCount < 0) {
    throw new Error(`Failed to navigate to ${url} after 3 retries.`);
  }
  await Promise.all([
    page.goto(url, {
      timeout: 120 * 1000,
      waitUntil: 'load',
    }),
    page.waitForResponse((response) => response.ok(), { timeout: 8000 }),
  ]).catch(() => {
    GotoWithRetry(page, url, retryCount - 1);
  });
};

0
投票

这是一个没有厄运金字塔的重试示例。 虽然我不是 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
接受一个回调,并且必须返回一个promise。
callback
会一直执行,直到 Promise 完成(解决、拒绝)或达到
retries
的数量。

© www.soinside.com 2019 - 2024. All rights reserved.