简体   繁体   English

表单提交后 Puppeteer 等待页面加载

[英]Puppeteer wait page load after form submit

I submit a form using the following code and i want Puppeteer to wait page load after form submit.我使用以下代码提交表单,我希望 Puppeteer 在表单提交后等待页面加载。

await page.click("button[type=submit]");

//how to wait until the new page loads before taking screenshot?
// i don't want this:
// await page.waitFor(1*1000);  //← unwanted workaround
await page.screenshot({path: 'example.png'});

How to wait for page load with puppeteer?如何使用 puppeteer 等待页面加载?

You can wait for navigation asynchronously to avoid getting null on redirection,您可以异步等待导航以避免在重定向时获得null

await Promise.all([
      page.click("button[type=submit]"),
      page.waitForNavigation({ waitUntil: 'networkidle0' }),
]);

This will help you if the page.click already triggers a navigation.如果 page.click 已触发导航,这将对您有所帮助。

await page.waitForNavigation();

According to the Official Documentation , you should use:根据官方文档,您应该使用:

page.waitForNavigation(options) page.waitForNavigation(选项)

  • options < Object > Navigation parameters which might have the following properties: options < Object > 导航参数可能具有以下属性:
    • timeout < number > Maximum navigation time in milliseconds, defaults to 30 seconds, pass 0 to disable timeout. timeout < number > 最大导航时间,以毫秒为单位,默认为 30 秒,传递0以禁用超时。 The default value can be changed by using the page.setDefaultNavigationTimeout(timeout) method.可以使用page.setDefaultNavigationTimeout(timeout)方法更改默认值。
    • waitUntil < string | waitUntil < 字符串| Array < string >> When to consider navigation succeeded, defaults to load . Array < string >> 何时认为导航成功,默认为load Given an array of event strings, navigation is considered to be successful after all events have been fired.给定一组事件字符串,在触发所有事件后,导航被认为是成功的。 Events can be either:事件可以是:
      • load - consider navigation to be finished when the load event is fired. load - 考虑在触发load事件时完成导航。
      • domcontentloaded - consider navigation to be finished when the DOMContentLoaded event is fired. domcontentloaded - 考虑在触发DOMContentLoaded事件时完成导航。
      • networkidle0 - consider navigation to be finished when there are no more than 0 network connections for at least 500 ms. networkidle0 - 当至少500毫秒没有超过 0 个网络连接时,认为导航已完成。
      • networkidle2 - consider navigation to be finished when there are no more than 2 network connections for at least 500 ms. networkidle2 - 当至少500毫秒没有超过 2 个网络连接时,认为导航已完成。
  • returns: < Promise <[?Response]>> Promise which resolves to the main resource response.返回:< Promise <[?Response]>> Promise 解析为主要资源响应。 In case of multiple redirects, the navigation will resolve with the response of the last redirect.在多次重定向的情况下,导航将使用上次重定向的响应进行解析。 In case of navigation to a different anchor or navigation due to History API usage, the navigation will resolve with null .如果由于 History API 的使用而导航到不同的锚点或导航,导航将解析为null

Readability:可读性:

You can use page.waitForNavigation() to wait for a page to navigate:您可以使用page.waitForNavigation()等待页面导航:

await page.waitForNavigation();

Performance:性能:

But since page.waitForNavigation() is a shortcut for page.mainFrame().waitForNavigation() , we can use the following for a minor performance enhancement:但是由于page.waitForNavigation()page.mainFrame().waitForNavigation()的快捷方式,我们可以使用以下内容来提高性能:

await page._frameManager._mainFrame.waitForNavigation();

Sometimes even using await page.waitForNavigation() will still result in a Error: Execution context was destroyed, most likely because of a navigation.有时即使使用await page.waitForNavigation()仍然会导致Error: Execution context was destroyed, most likely because of a navigation.

In my case, it was because the page was redirecting multiple times.就我而言,这是因为页面多次重定向。 The API says the default waitUntil option is Load —this required me to wait for navigation each redirect (3 times).API表示默认waitUntil选项是Load -这需要我等待导航每个重定向(3次)。

Using only a single instance of page.waitForNavigation with the waitUntil option networkidle2 worked well in my case:只使用单个实例page.waitForNavigationwaitUntil选项networkidle2在我的情况下工作良好:

await button.click();

await page.waitForNavigation({waitUntil: 'networkidle2'});

Finally, the API suggests using a Promise.All to prevent a race condition.最后,API 建议使用Promise.All来防止竞争条件。 I haven't needed this but provide it for completeness :我不需要这个,但提供它是为了完整性

await Promise.all([button.click(), page.waitForNavigation({waitUntil:'networkidle2'})])

If all else fails, you can use page.waitForSelector as recommended on a Puppeteer github issue —or in my case, page.waitForXPath()如果所有其他方法都失败了,您可以按照Puppeteer github 问题上的建议使用page.waitForSelector — 或者在我的情况下使用page.waitForXPath()

I know it is bit late to answer this.我知道现在回答这个有点晚了。 It may be helpful for those who are getting below exception while doing waitForNavigation .对于那些在执行waitForNavigation 时遇到异常情况的人可能会有所帮助。

(node:14531) UnhandledPromiseRejectionWarning: TimeoutError: Navigation Timeout Exceeded: 30000ms exceeded at Promise.then (/home/user/nodejs/node_modules/puppeteer/lib/LifecycleWatcher.js:142:21) at -- ASYNC -- at Frame. (node:14531) UnhandledPromiseRejectionWarning: TimeoutError: Navigation Timeout Exceeded: Promise.then (/home/user/nodejs/node_modules/puppeteer/lib/LifecycleWatcher.js:142:21) at -- ASYNC -- at Frame 超过 30000ms。 (/home/user/nodejs/node_modules/puppeteer/lib/helper.js:111:15) at Page.waitForNavigation (/home/user/nodejs/node_modules/puppeteer/lib/Page.js:649:49) at Page. (/home/user/nodejs/node_modules/puppeteer/lib/helper.js:111:15) 在 Page.waitForNavigation (/home/user/nodejs/node_modules/puppeteer/lib/Page.js:649:49) 在 Page . (/home/user/nodejs/node_modules/puppeteer/lib/helper.js:112:23) at /home/user/nodejs/user/puppeteer/example7.js:14:12 at (/home/user/nodejs/node_modules/puppeteer/lib/helper.js:112:23) 在 /home/user/nodejs/user/puppeteer/example7.js:14:12 在

The correct code that worked for me is as below.对我有用的正确代码如下。

await page.click('button[id=start]', {waitUntil: 'domcontentloaded'});

Similarly if you are going to a new page, code should be like同样,如果你要进入一个新页面,代码应该像

await page.goto('here goes url', {waitUntil: 'domcontentloaded'});

i suggest to wrap page.to in a wrapper and wait for everything loaded我建议将 page.to 包装在一个包装器中并等待加载所有内容

this is my wrapper这是我的包装纸

loadUrl: async function (page, url) {
    try {
        await page.goto(url, {
            timeout: 20000,
            waitUntil: ['load', 'domcontentloaded', 'networkidle0', 'networkidle2']
        })
    } catch (error) {
        throw new Error("url " + url + " url not loaded -> " + error)
    }
}

now you can use this with现在你可以使用它

await loadUrl(page, "https://www.google.com")
await Promise.all([
      page.click(selectors.submit),
      page.waitForNavigation({ waitUntil: 'networkidle0' }),
]);

This would be the first priority to use as it waits for all network to complete and assumes it is done when you don't have more than 0 network call for 500ms.这将是使用的第一优先级,因为它等待所有网络完成并假设它在 500 毫秒内没有超过 0 个网络调用时完成。

you can also use你也可以使用

await page.waitForNavigation({ waitUntil: 'Load' })

or else, you can use否则,你可以使用

await page.waitForResponse(response => response.ok())

this function can also be used in various places as it only allows to proceed further when all the calls are a success that is when all the response status is ok ie (200-299)这个函数也可以用在不同的地方,因为它只允许在所有调用都成功时,即所有响应状态都正常时,即 (200-299) 继续进行

This worked for me:这对我有用:

await Promise.all([
    page.goto(URL),
    page.waitForNavigation({ waitUntil: 'networkidle0' }),
]);
console.log('page loaded')

For some reason I was not able to click button (Handled an event, not in form)出于某种原因,我无法点击按钮(处理了一个事件,而不是在表单中)

<button onclick="someFunction();" class="button button2">Submit</button>

The problem was that page was rendered on server side.问题是页面是在服务器端呈现的。 Thus the button didn't existed whenever I waited for input field await page.waitForSelector('button.button2')因此,每当我等待输入字段时,按钮都不存在await page.waitForSelector('button.button2')

The solution was to bind page.goto(URL) and page.waitForNavigation({ waitUntil: 'networkidle0' }) in Promise解决方案是在Promise绑定page.goto(URL)page.waitForNavigation({ waitUntil: 'networkidle0' })

await Promise.all([
    page.goto(URL),
    page.waitForNavigation({ waitUntil: 'networkidle0' }),
]);
console.log('page loaded')

await page.waitForSelector('button.button2')
console.log('button is here');

I ran into a scenario, where there was the classic POST-303-GET and an input[type=submit] was involved.我遇到了一个场景,其中有经典的 POST-303-GET 并且涉及input[type=submit] It seems that in this case, the click of the button won't resolve until after the associated form's submission and redirection, so the solution was to remove the waitForNavigation , because it was executed after the redirection and thus was timing out.在这种情况下,按钮的click似乎在关联表单提交和重定向之后才会解析,因此解决方案是删除waitForNavigation ,因为它是在重定向之后执行的,因此超时。

If submitting the form opens some other page, then you may just want to wait for a selector in that page.如果提交表单会打开其他页面,那么您可能只想等待该页面中的选择器。 I have often had issues using page.waitForNavigation() since it's options don't really ensure we have effectively navigated to another page.我经常在使用page.waitForNavigation()遇到问题,因为它的选项并不能真正确保我们有效地导航到另一个页面。

// login page
page.click("#login");
// homepage, after login
page.waitForSelector("#home", {visible: true}); // page.waitForXpath()

Of you course you can increase the wait time for the selector.当然,您可以增加选择器的等待时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM