Puppeteer 在返回前未触发点击 HTML
Puppeteer Not Triggering Click Before Returning HTML
我的 Node.js puppeteer 脚本成功填写了一个表单,但该页面在返回前只接受 some 元素上的 "click" 事件修改后的页面内容。这是脚本:
const fetchContracts = async (url) => {
const browser = await pupeteer.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox']});
const page = await browser.newPage();
const pendingXHR = new PendingXHR(page);
await page.goto(url, { waitUntil: 'networkidle2' });
await Promise.all([
page.click("#agree_statement"),
page.waitForNavigation()
]);
await page.click(".form-check-input");
await Promise.all([
page.click(".btn-primary"),
page.waitForNavigation()
]);
/// MY PROBLEM OCCURS HERE
/// Sometimes these clicks do not register....
await page.click('#filedReports th:nth-child(5)')
await pendingXHR.waitForAllXhrFinished();
await page.click('#filedReports th:nth-child(5)');
await pendingXHR.waitForAllXhrFinished();
/// And my bot skips directly here....
let html = await page.content();
await page.close();
await browser.close();
return html;
}
"pendingXHR" 模块是一个导入,我将其从 this 库的代码中拉入顶部:
const { PendingXHR } = require('pending-xhr-puppeteer');
该脚本在我的本地计算机上运行,并且在我将脚本上传到 Digital Ocean 时 一些 运行。根据我正在抓取的页面,这些点击启动了我正在等待的 XHR 请求。证明如下:
所以我的问题是:
为什么这些点击 不会 注册,即使我正在等待它们并等待 XHR 请求,在 html 从页面中拉出然后返回之前?为什么与此不一致,有时 注册了点击,有时却没有?
感谢您的帮助。
您是否尝试过以下解决方法:
await page.waitfor(1000);// this line will wait for 1 Sec
这样你就可以确定它已加载
更好的方法是将 page.click 放在 Promise.all 中,像这样:
await Promise.all([
await page.click('#filedReports th:nth-child(5)'),
await pendingXHR.waitForAllXhrFinished()
]);
PS:您在
处缺少一个分号
/// MY PROBLEM OCCURS HERE
/// Sometimes these clicks do not register....
\/
await page.click('#filedReports th:nth-child(5)')
await pendingXHR.waitForAllXhrFinished(); /\
await page.click('#filedReports th:nth-child(5)');
await pendingXHR.waitForAllXhrFinished();
简短回答:点击将导致延迟的 AJAX 请求,因此 pendingXHR.waitForAllXhrFinished()
将立即解决,因为当时没有请求发生函数被执行。请改用 page.waitForResponse('.../data/')
。
问题
您期望发生以下事件过程:
- 发生点击
- AJAX 请求开始
pendingXHR.waitForAllXhrFinished()
执行
- AJAX 请求完成
- Table 被渲染
pendingXHR.waitForAllXhrFinished()
解决
page.content()
执行
问题是您正在使用的库 (PendingXHR) waits for the currently pending requests 会在解决后立即解决。这在我能想到的两种情况下不起作用:
1. AJAX请求异步启动
在这种情况下,事件的顺序是这样的:
- 发生点击,但异步启动 AJAX 调用(稍后)
pendingXHR.waitForAllXhrFinished()
执行
pendingXHR.waitForAllXhrFinished()
立即解决(因为没有请求)
page.content()
执行(太早了!)
- AJAX 请求开始
- AJAX 请求完成
- Table 被渲染
2。 UI 异步修改 table
在这种情况下,事件的顺序是这样的:
- 发生点击
- AJAX 请求开始
pendingXHR.waitForAllXhrFinished()
执行
- AJAX 请求完成(但代码稍后呈现 table)
pendingXHR.waitForAllXhrFinished()
解决
page.content()
(太早了!)
- Table 被渲染
不一致的发生是因为有时事件的顺序可能是正确的,因为在这种情况下毫秒可以决定先发生什么。
修复
不看页面代码,我不能确定是哪种情况(实际上可能是两种情况),但我猜这是第一种,因为我完全可以看到 table 库等待任何双 clicks/dragging/etc。在发出 AJAX 请求之前发生。
第一个问题可以通过使用 page.waitForResponse
而不是 pendingXHR.waitForAllXhrFinished
来解决,因为这可以确保对 data/
的请求确实发生了。
修复第二种情况(如有必要)并非易事,但可以通过使用 page.waitFor(10)
.
引入固定等待时间来完成
通过修复这两种情况,新代码如下所示:
await Promise.all([ // wait for the response to happen and click
page.waitForResponse('.../data/'), // use the actual URL here
page.click('...'),
]);
await page.waitFor(10); // wait for any asynchronous rerenders that might happen
let html = await page.content();
我的 Node.js puppeteer 脚本成功填写了一个表单,但该页面在返回前只接受 some 元素上的 "click" 事件修改后的页面内容。这是脚本:
const fetchContracts = async (url) => {
const browser = await pupeteer.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox']});
const page = await browser.newPage();
const pendingXHR = new PendingXHR(page);
await page.goto(url, { waitUntil: 'networkidle2' });
await Promise.all([
page.click("#agree_statement"),
page.waitForNavigation()
]);
await page.click(".form-check-input");
await Promise.all([
page.click(".btn-primary"),
page.waitForNavigation()
]);
/// MY PROBLEM OCCURS HERE
/// Sometimes these clicks do not register....
await page.click('#filedReports th:nth-child(5)')
await pendingXHR.waitForAllXhrFinished();
await page.click('#filedReports th:nth-child(5)');
await pendingXHR.waitForAllXhrFinished();
/// And my bot skips directly here....
let html = await page.content();
await page.close();
await browser.close();
return html;
}
"pendingXHR" 模块是一个导入,我将其从 this 库的代码中拉入顶部:
const { PendingXHR } = require('pending-xhr-puppeteer');
该脚本在我的本地计算机上运行,并且在我将脚本上传到 Digital Ocean 时 一些 运行。根据我正在抓取的页面,这些点击启动了我正在等待的 XHR 请求。证明如下:
所以我的问题是:
为什么这些点击 不会 注册,即使我正在等待它们并等待 XHR 请求,在 html 从页面中拉出然后返回之前?为什么与此不一致,有时 注册了点击,有时却没有?
感谢您的帮助。
您是否尝试过以下解决方法:
await page.waitfor(1000);// this line will wait for 1 Sec
这样你就可以确定它已加载 更好的方法是将 page.click 放在 Promise.all 中,像这样:
await Promise.all([
await page.click('#filedReports th:nth-child(5)'),
await pendingXHR.waitForAllXhrFinished()
]);
PS:您在
处缺少一个分号
/// MY PROBLEM OCCURS HERE
/// Sometimes these clicks do not register....
\/
await page.click('#filedReports th:nth-child(5)')
await pendingXHR.waitForAllXhrFinished(); /\
await page.click('#filedReports th:nth-child(5)');
await pendingXHR.waitForAllXhrFinished();
简短回答:点击将导致延迟的 AJAX 请求,因此 pendingXHR.waitForAllXhrFinished()
将立即解决,因为当时没有请求发生函数被执行。请改用 page.waitForResponse('.../data/')
。
问题
您期望发生以下事件过程:
- 发生点击
- AJAX 请求开始
pendingXHR.waitForAllXhrFinished()
执行- AJAX 请求完成
- Table 被渲染
pendingXHR.waitForAllXhrFinished()
解决page.content()
执行
问题是您正在使用的库 (PendingXHR) waits for the currently pending requests 会在解决后立即解决。这在我能想到的两种情况下不起作用:
1. AJAX请求异步启动
在这种情况下,事件的顺序是这样的:
- 发生点击,但异步启动 AJAX 调用(稍后)
pendingXHR.waitForAllXhrFinished()
执行pendingXHR.waitForAllXhrFinished()
立即解决(因为没有请求)page.content()
执行(太早了!)- AJAX 请求开始
- AJAX 请求完成
- Table 被渲染
2。 UI 异步修改 table
在这种情况下,事件的顺序是这样的:
- 发生点击
- AJAX 请求开始
pendingXHR.waitForAllXhrFinished()
执行- AJAX 请求完成(但代码稍后呈现 table)
pendingXHR.waitForAllXhrFinished()
解决page.content()
(太早了!)- Table 被渲染
不一致的发生是因为有时事件的顺序可能是正确的,因为在这种情况下毫秒可以决定先发生什么。
修复
不看页面代码,我不能确定是哪种情况(实际上可能是两种情况),但我猜这是第一种,因为我完全可以看到 table 库等待任何双 clicks/dragging/etc。在发出 AJAX 请求之前发生。
第一个问题可以通过使用 page.waitForResponse
而不是 pendingXHR.waitForAllXhrFinished
来解决,因为这可以确保对 data/
的请求确实发生了。
修复第二种情况(如有必要)并非易事,但可以通过使用 page.waitFor(10)
.
通过修复这两种情况,新代码如下所示:
await Promise.all([ // wait for the response to happen and click
page.waitForResponse('.../data/'), // use the actual URL here
page.click('...'),
]);
await page.waitFor(10); // wait for any asynchronous rerenders that might happen
let html = await page.content();