在 puppeteer/JavaScript 中重试 page.goto、page.waitForNavigation 等的最佳实践
Best Practice for retrying page.goto, page.waitForNavigation etc. in puppeteer/JavaScript
我正在尝试使用 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); });
但我认为经常重试一次或多次将有助于最终获得所需的资源。是否有实施重试的最佳实践?
这是一个没有厄运金字塔重试的例子。
虽然我不是 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
接受回调,必须 return 一个承诺。 callback
被执行直到 Promise 完成(解决、拒绝)或达到 retries
的数量。
我会推荐这种相当简单的方法:
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);
}
我正在尝试使用 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); });
但我认为经常重试一次或多次将有助于最终获得所需的资源。是否有实施重试的最佳实践?
这是一个没有厄运金字塔重试的例子。 虽然我不是 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
接受回调,必须 return 一个承诺。 callback
被执行直到 Promise 完成(解决、拒绝)或达到 retries
的数量。
我会推荐这种相当简单的方法:
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);
}