Puppeteer 在返回 HTML 之前不触发点击

Har*_*mer 5 javascript events xmlhttprequest node.js puppeteer

我的 Node.js puppeteer 脚本成功地填写了一个表单,但在返回修改后的页面内容之前,页面有时只接受元素上的“单击”事件。这是脚本:

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;
Run Code Online (Sandbox Code Playgroud)

}

“pendingXHR”模块是一个导入,我从这个库的代码中把它放在最上面:

const { PendingXHR } = require('pending-xhr-puppeteer');
Run Code Online (Sandbox Code Playgroud)

该脚本在我的本地计算机上运行,​​并且在我将脚本上传到 Digital Ocean 时有时会运行。根据我正在抓取的页面,这些点击会启动 XHR 请求,我正在尝试等待。这里有证据:

在此处输入图片说明

所以我的问题是:

为什么这些点击不会注册,即使我正在等待它们并等待 XHR 请求,然后才从页面中提取 html 然后返回?为什么与此不一致,有时点击注册,有时不注册?

谢谢你的帮助。

Tho*_*orf 3

简短回答:单击将导致 AJAX 请求延迟,因此pendingXHR.waitForAllXhrFinished()将立即解决,因为执行函数时没有发生任何请求。page.waitForResponse('.../data/')代替使用。

问题

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

  1. 点击发生
  2. AJAX 请求开始
  3. pendingXHR.waitForAllXhrFinished()被处决
  4. AJAX请求完成
  5. 表格已渲染
  6. pendingXHR.waitForAllXhrFinished()决心
  7. page.content()被处决

问题是您正在使用的库 (PendingXHR)等待当前待处理的请求,并在它们解决后立即解决。这在我能想到的两种情况下不起作用:

1.AJAX请求异步启动

在这种情况下,事件的顺序将如下所示:

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

2、UI异步修改表

在这种情况下,事件的顺序将如下所示:

  1. 点击发生
  2. AJAX 请求开始
  3. pendingXHR.waitForAllXhrFinished()被处决
  4. AJAX 请求完成(但代码稍后呈现表格)
  5. pendingXHR.waitForAllXhrFinished()决心
  6. page.content() (太早了!)
  7. 表格已渲染

不一致的发生是因为有时事件的顺序可能是正确的,因为在这种情况下,毫秒可以决定首先发生什么。

使固定

在不查看页面代码的情况下,我无法确定是哪种情况(实际上可能是两种情况),但我猜这是第一个,因为我完全可以看到表库等待任何双击/拖动/等等。在发出 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();
Run Code Online (Sandbox Code Playgroud)

  • 谢谢。这解释得非常好,我将不再依赖第三方库,因为我知道这个 page.waitForResponse 存在,并且我可以使用它来等待特定资源。 (2认同)