我正在尝试使用puppeteer浏览SPA,我在这里遇到的问题是我无法等待页面加载然后继续我的程序。
我填写表单,然后点击提交,根据表单的内容,可以加载不同的页面,因此我不能使用page.waitFor(Selector)
,因为根据输入可能有许多不同的页面。
我尝试使用waitUntil:
load,networkidle2,networkidle0,domcontentloaded,但所有这些都在元素加载之前触发。
我尝试自动化的页面是Link。 (如果您想自己检查,请选择预订参考并填写随机详细信息,然后按继续。)
在链接中选择“预订参考”后,我用木偶操作员填写详细信息,然后按“继续”按钮,我无法弄清楚如何在不依赖选择器的情况下等待页面完全加载。
答案 0 :(得分:2)
我认为您应该知道这些网页是什么,并为每个页面使用Promise.race
和page.waitFor
,如下所示:
const puppeteer = require('puppeteer');
const html = `
<html>
<body>
<div id="element"></div>
<button id="button">load</button>
<script>
document.getElementById('button').addEventListener("click", () => {
document.getElementById('element').innerHTML =
'<div id="element' + (Math.floor(Math.random() * 3) + 1) + '"></div>';
});
</script>
</body>
</html>`;
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(`data:text/html,${html}`);
await page.click('#button');
const element = await Promise.race([
page.waitFor('#element1'),
page.waitFor('#element2'),
page.waitFor('#element3')
]);
console.log(await (await element.getProperty('id')).jsonValue());
await browser.close();
})();
答案 1 :(得分:1)
对于那些寻求快速解答的人,下面是主要代码:
await Promise.all([page.waitForNavigation(), el.click()]);
......,其中el
是指向SPA中另一个页面的链接,而click
可以是导致导航的任何事件。有关详情,请参见下文。
我同意waitFor
不能太有用,如果您不能依赖页面内容。即使可以,在大多数情况下,这似乎也不如自然地对导航做出反应那样可取。幸运的是,page.waitForNavigation
确实适用于SPA。这是一个最小的完整示例,该示例是在一个使用history API(下面的index.html
)的微型SPA样机上,使用链接上的click事件在页面之间进行导航(同样适用于表单提交)。我使用了Node 10和Puppeteer 5.4.1。
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<script>
const nav = `<a href="/">Home</a> | <a href="/about">About</a> |
<a href="/contact">Contact</a>`;
const routes = {
"/": `<h1>Home</h1>${nav}<p>Welcome home!</p>`,
"/about": `<h1>About</h1>${nav}<p>This is a tiny SPA</p>`,
};
const render = path => {
document.body.innerHTML = routes[path] || `<h1>404</h1>${nav}`;
document.querySelectorAll('[href^="/"]').forEach(el =>
el.addEventListener("click", evt => {
evt.preventDefault();
const {pathname: path} = new URL(evt.target.href);
window.history.pushState({path}, path, path);
render(path);
})
);
};
window.addEventListener("popstate", e =>
render(new URL(window.location.href).pathname)
);
render("/");
</script>
</body>
</html>
index.js
:const puppeteer = require("puppeteer");
(async () => {
const browser = await puppeteer.launch(/*{dumpio: true}*/);
const page = await browser.newPage();
// navigate to the home page for the SPA and print the contents
await page.goto("http://localhost:8000");
console.log(await page.url());
console.log(await page.$eval("p", el => el.innerHTML));
// navigate to the about page via the link
const [el] = await page.$x('//a[text()="About"]');
await Promise.all([page.waitForNavigation(), el.click()]);
// print proof that we're on the about page
console.log(await page.url());
console.log(await page.$eval("p", el => el.innerHTML));
await browser.close();
})();
$ python3 -m http.server &
$ node index.js
http://localhost:8000/
Welcome home!
http://localhost:8000/about
This is a tiny SPA
如果await Promise.all([page.waitForNavigation(), el.click()]);
模式看起来很奇怪,请参见this issue thread,它解释了gotcha直观
await page.waitForNavigation();
await el.click();
导致比赛情况。
可以通过以下方法完成与上面显示的Promise.all
相同的事情:
const navPromise = page.waitForNavigation({timeout: 1000});
await el.click();
await navPromise;
有关使用Puppeteer(包括散列路由器)导航SPA的更多信息,请参见此related answer。
答案 2 :(得分:0)
单页应用程序等待导航并获取响应状态和数据的解决方法。无论是使用 fetch
还是 XHR
来执行 Ajax
请求,主要思想都应该是相同的。以下示例使用 fetch
async spaClick (selector) {
const res = await this.eval(selector, el => {
window.originalFetch = window.originalFetch || window.fetch
return new Promise(resolve => {
window.fetch = function (...args) {
return window.originalFetch.apply(this, args)
.then(async response => {
resolve({
status: response.status,
data: await response.clone().text()
})
return response
})
}
el.click()
})
})
if (!res) throw new Error('spaClick() Navigation triggered before eval resolves!')
return res
}
const puppeteer = require('puppeteer');
const url = 'http://www.faalkaart.nl';
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// await Promise.all([
// page.waitForNavigation({ waitUntil: 'networkidle0' }),
// page.click('selector-that-triggers-navigation'),
// ]);
const response = await spaClick('selector-that-triggers-navigation')
console.log(response) // {status, data}
await browser.close();
})();