Кукольник не вызывает щелчок перед возвратом HTML - PullRequest
4 голосов
/ 27 марта 2019

Мой сценарий кукловода Node.js успешно заполняет форму, но страница принимает только событие «щелчка» для элемента через некоторое время перед возвратом измененного содержимого страницы. Вот сценарий:

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" - это импорт, который я вытащил вверх в своем коде из этой библиотеки:

const { PendingXHR } = require('pending-xhr-puppeteer');

Скрипт работает на моем локальном компьютере и работает некоторое времени, когда я загружаю скрипт в Digital Ocean. Согласно странице, которую я сканирую, эти клики инициируют запросы XHR, которые я пытаюсь дождаться. Вот доказательство:

enter image description here

Итак, мой вопрос:

Почему эти клики не регистрируются, хотя я жду их и жду запросов XHR, прежде чем HTML будет извлечен со страницы, а затем возвращен? И почему несоответствие этому, где иногда клики регистрируются, а иногда нет?

Спасибо за вашу помощь.

Ответы [ 2 ]

1 голос
/ 27 марта 2019

Краткий ответ: Щелчок приведет к отложенному 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. Пользовательский интерфейс изменяет таблицу асинхронно

В этом случае порядок событий будет таким:

  1. Клик происходит
  2. AJAX-запрос запускается
  3. pendingXHR.waitForAllXhrFinished() выполнено
  4. AJAX-запрос завершается (но код отображает таблицу позже)
  5. pendingXHR.waitForAllXhrFinished() разрешает
  6. page.content() (слишком рано!)
  7. Таблица отображается

Несоответствие происходит, поскольку иногда события могут быть в правильном порядке, поскольку в этом случае миллисекунда может решить, что произойдет первым.

Fix

Не глядя на код страницы, я не могу сказать, в каком случае это точно (это может быть на самом деле и то и другое), но я бы предположил, что это первый случай, так как я могу полностью увидеть библиотеку таблиц, ожидающую любого двойной щелчок / перетаскивание / и т.д. произойдет до того, как он сделает запрос 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();
1 голос
/ 27 марта 2019

Вы пытались сделать обходной путь, например:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...