错误起因:
错误一:
"Execution context was destroyed, most likely because of a navigation"错误通常发生在使用 Puppeteer 或类似工具时,尝试在页面导航后继续操作页面元素或执行其他操作时。这个错误的主要原因是页面导航导致当前页面的执行环境被销毁,而后续的操作仍然在尝试使用已经销毁的页面上下文,从而导致错误。
错误二:
”TimeoutError: Navigation timeout of 120000 ms exceeded“这个错误通常是由页面导航超时引起的。它意味着 Puppeteer 在规定的时间内无法完成页面加载。在你的情况下,超时时间设置为 120000 毫秒(即 2 分钟),但页面加载时间超过了这个阈值。
处理方法
Puppeteer 提供了多种超时相关的操作,用于控制在执行自动化测试或网页操作时的超时行为。以下是 Puppeteer 中常用的超时相关操作:
-
page.setDefaultTimeout(timeout)
:- 设置页面操作的默认超时时间,单位为毫秒。如果操作未在超时时间内完成,将会抛出错误。默认超时时间为 30 秒。
-
page.setDefaultNavigationTimeout(timeout)
:- 设置页面导航的默认超时时间,单位为毫秒。如果页面导航未在超时时间内完成,将会抛出错误。默认超时时间为 30 秒。
-
page.waitForTimeout(timeout)
:- 在页面上等待指定的时间,单位为毫秒。这不是一个超时操作,而是一个简单的等待操作。
-
page.waitForNavigation(options)
:- 等待页面导航完成,可以设置超时时间和其他选项。如果页面导航未在超时时间内完成,将会抛出错误。
-
page.waitForSelector(selector[, options])
:- 等待指定的 CSS 选择器匹配到一个元素,可以设置超时时间和其他选项。如果选择器未在超时时间内匹配到元素,将会抛出错误。
-
page.waitForXPath(xpath[, options])
:- 等待匹配指定 XPath 表达式的元素出现,可以设置超时时间和其他选项。如果 XPath 表达式未在超时时间内匹配到元素,将会抛出错误。
-
page.waitForFunction(pageFunction, options[, ...args])
:- 等待页面上的 JavaScript 函数返回 true,可以设置超时时间和其他选项。如果函数未在超时时间内返回 true,将会抛出错误。
-
page.waitForRequest(urlOrPredicate[, options])
和page.waitForResponse(urlOrPredicate[, options])
:- 等待特定的网络请求或响应,可以设置超时时间和其他选项。如果请求或响应未在超时时间内完成,将会抛出错误。
实例列表
函数:waitForNavigation(options)
解释:
等待框架完成导航。当你运行的代码间接导致框架导航时,这个函数非常有用。使用 History API 改变 URL 被视为一次导航。
示例:
const [response] = await Promise.all([
// 导航完成后,这个 promise 解析
frame.waitForNavigation(),
// 点击链接间接导致了一次导航
frame.click('a.my-link'),
]);
在这个示例中,使用了 waitForNavigation
函数等待框架导航完成。Promise.all
包裹了两个异步操作,其中之一是点击链接,以确保等待正确地同步。
函数:waitForSelector(selector, options)
解释:
等待当前页面中匹配指定选择器的元素出现。与 Frame.waitForSelector
不同,此方法不会跨页面导航,也不会在元素从 DOM 中移除后继续等待。
示例:
import puppeteer from 'puppeteer';
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
let currentURL;
page
.mainFrame()
.waitForSelector('img')
.then(() => console.log('第一个带有图片的 URL: ' + currentURL));
for (currentURL of [
'https://example.com',
'https://google.com',
'https://bbc.com',
]) {
await page.goto(currentURL);
}
await browser.close();
})();
函数:waitForXPath(xpath, options)
解释:
等待当前页面中匹配给定 XPath 表达式的元素出现。此方法可以跨页面导航,并且如果在调用该方法时 XPath 已经存在,则立即返回。
示例:
import puppeteer from 'puppeteer';
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
let currentURL;
page
.waitForXPath('//img')
.then(() => console.log('第一个带有图片的 URL: ' + currentURL));
for (currentURL of [
'https://example.com',
'https://google.com',
'https://bbc.com',
]) {
await page.goto(currentURL);
}
await browser.close();
})();
这两个示例展示了如何使用 waitForSelector 和 waitForXPath 等待页面上特定元素的出现。在每次页面加载完成后,代码都会等待页面上的图片元素出现,然后输出第一个带有图片的 URL。
函数:waitForFunction(pageFunction, options)
解释:
等待页面上下文中的函数执行完成。
示例:
import puppeteer from 'puppeteer';
// 观察视口大小变化
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
const watchDog = page.waitForFunction('window.innerWidth < 100');
await page.setViewport({width: 50, height: 50});
await watchDog;
await browser.close();
})();
// 从 Node.js 传递参数到 waitForFunction 函数的断言
const selector = '.foo';
await page.waitForFunction(
selector => !!document.querySelector(selector),
{},
selector
);
// waitForFunction 的断言也可以是异步的
const username = 'github-username';
await page.waitForFunction(
async username => {
const githubResponse = await fetch(
`https://api.github.com/users/${username}`
);
const githubUser = await githubResponse.json();
// 展示头像
const img = document.createElement('img');
img.src = githubUser.avatar_url;
// 等待 3 秒
await new Promise((resolve, reject) => setTimeout(resolve, 3000));
img.remove();
},
{},
username
);
在这个示例中,waitForFunction
函数被用来等待页面上下文中的函数执行完成。第一个示例观察了视口大小的变化,第二个示例传递了参数给断言函数,第三个示例展示了断言函数可以是异步的情况。
函数:waitForRequest(urlOrPredicate, options)
解释:
等待匹配给定 URL 或断言的请求。
示例:
const firstRequest = await page.waitForRequest(
'https://example.com/resource'
);
const finalRequest = await page.waitForRequest(
request => request.url() === 'https://example.com'
);
return finalRequest.response()?.ok();
在这个示例中,waitForRequest
函数用于等待与给定 URL 匹配的请求。第一个示例等待第一个请求完成,第二个示例使用断言等待特定的请求。
函数:waitForResponse(urlOrPredicate, options)
解释:
等待匹配给定 URL 或断言的响应。
示例:
const firstResponse = await page.waitForResponse(
'https://example.com/resource'
);
const finalResponse = await page.waitForResponse(
response =>
response.url() === 'https://example.com' && response.status() === 200
);
const finalResponse = await page.waitForResponse(async response => {
return (await response.text()).includes('<html>');
});
return finalResponse.ok();
在这个示例中,waitForResponse
函数用于等待与给定 URL 匹配的响应。第一个示例等待第一个响应完成,第二个示例使用断言等待特定的响应,第三个示例使用异步断言等待满足条件的响应。
函数:waitForFrame(urlOrPredicate, options)
解释:
等待满足给定条件的框架出现。
示例:
const frame = await page.waitForFrame(async frame => {
return frame.name() === 'Test';
});
在这个示例中,waitForFrame
函数用于等待满足条件的框架出现。它可以接受一个异步断言函数作为参数,用于判断框架是否满足条件。
函数:waitForNetworkIdle(options)
解释:
等待网络空闲状态。
示例:
await page.waitForNetworkIdle();
在这个示例中,waitForNetworkIdle
函数用于等待网络处于空闲状态。
函数:waitForDevicePrompt(options)
解释:
此方法通常与触发来自 WebBluetooth 等 API 的设备请求的操作耦合。
示例:
const [devicePrompt] = Promise.all([
page.waitForDevicePrompt(),
page.click('#connect-bluetooth'),
]);
await devicePrompt.select(
await devicePrompt.waitForDevice(({name}) => name.includes('My Device'))
);
这个示例中,waitForDevicePrompt
函数用于等待设备提示。在点击连接蓝牙按钮后,它选择了一个设备,并等待设备的出现。