木偶不触发单击,然后返回HTML

时间:2019-03-27 03:37:15

标签: javascript node.js events xmlhttprequest puppeteer

我的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时可以运行 some 。根据我正在爬网的页面,这些单击会启动XHR请求,而我正试图等待这些请求。这是证明:

enter image description here

所以我的问题是:

为什么在从页面中提取html然后返回html之前,即使我正在等待它们并等待XHR请求,这些点击还是会注册?以及为什么与此不一致(有时在 处注册了点击,有时却没有注册)?

感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

您是否尝试执行以下解决方法:

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();

答案 1 :(得分:1)

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

问题

您期望发生以下事件:

  1. 点击发生
  2. AJAX请求开始
  3. pendingXHR.waitForAllXhrFinished()被执行
  4. AJAX请求完成
  5. 表格已呈现
  6. pendingXHR.waitForAllXhrFinished()解决
  7. page.content()被执行

问题是您使用的waits for the currently pending requests库(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();