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/')

问题

您期望发生以下事件过程:

  1. 发生点击
  2. AJAX 请求开始
  3. pendingXHR.waitForAllXhrFinished()执行
  4. AJAX 请求完成
  5. Table 被渲染
  6. pendingXHR.waitForAllXhrFinished() 解决
  7. page.content()执行

问题是您正在使用的库 (PendingXHR) waits for the currently pending requests 会在解决后立即解决。这在我能想到的两种情况下不起作用:

1. AJAX请求异步启动

在这种情况下,事件的顺序是这样的:

  1. 发生点击,但异步启动 AJAX 调用(稍后)
  2. pendingXHR.waitForAllXhrFinished()执行
  3. pendingXHR.waitForAllXhrFinished() 立即解决(因为没有请求)
  4. page.content()执行(太早了!)
  5. AJAX 请求开始
  6. AJAX 请求完成
  7. Table 被渲染

2。 UI 异步修改 table

在这种情况下,事件的顺序是这样的:

  1. 发生点击
  2. AJAX 请求开始
  3. pendingXHR.waitForAllXhrFinished()执行
  4. AJAX 请求完成(但代码稍后呈现 table)
  5. pendingXHR.waitForAllXhrFinished() 解决
  6. page.content() (太早了!)
  7. 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();