Кукловод дождитесь полной загрузки страницы

Я работаю над созданием PDF с веб-страницы.

Приложение, над которым я работаю, представляет собой одностраничное приложение.

Перепробовал много вариантов и предложений по https://github.com/GoogleChrome/puppeteer/issues/1412

Но это не работает

    const browser = await puppeteer.launch({
    executablePath: 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
    ignoreHTTPSErrors: true,
    headless: true,
    devtools: false,
    args: ['--no-sandbox', '--disable-setuid-sandbox']
});

const page = await browser.newPage();

await page.goto(fullUrl, {
    waitUntil: 'networkidle2'
});

await page.type('#username', 'scott');
await page.type('#password', 'tiger');

await page.click('#Login_Button');
await page.waitFor(2000);

await page.pdf({
    path: outputFileName,
    displayHeaderFooter: true,
    headerTemplate: '',
    footerTemplate: '',
    printBackground: true,
    format: 'A4'
});

Я хочу создать отчет в формате PDF, как только страница будет полностью загружена.

Я не хочу писать никаких задержек, например, await page.waitFor (2000);

Я не могу выполнить waitForSelector, потому что на странице есть диаграммы и графики, которые отображаются после вычислений.

Помощь будет оценена.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
63
0
123 249
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

Мне всегда нравится ждать селекторы, так как многие из них являются отличным индикатором того, что страница полностью загружена:

await page.waitForSelector('#blue-button');

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

Arch4Arts 28.02.2021 13:54

@ Arch4Arts вы должны создать свою собственную функцию щелчка, которая будет ждать вас, а также щелкнуть

Nicolás A. 02.04.2021 22:01
Ответ принят как подходящий

Вы можете использовать page.waitForNavigation(), чтобы дождаться полной загрузки новой страницы перед созданием PDF:

await page.goto(fullUrl, {
  waitUntil: 'networkidle0',
});

await page.type('#username', 'scott');
await page.type('#password', 'tiger');

await page.click('#Login_Button');

await page.waitForNavigation({
  waitUntil: 'networkidle0',
});

await page.pdf({
  path: outputFileName,
  displayHeaderFooter: true,
  headerTemplate: '',
  footerTemplate: '',
  printBackground: true,
  format: 'A4',
});

Если есть определенный динамически генерируемый элемент, который вы хотели бы включить в свой PDF-файл, рассмотрите возможность использования page.waitForSelector(), чтобы гарантировать, что содержимое будет видимым:

await page.waitForSelector('#example', {
  visible: true,
});

Где документация для сигнала networkidle0?

Mathias S 27.08.2019 19:31

'networkidle0' задокументирован здесь github.com/GoogleChrome/puppeteer/blob/master/docs/…

sapeish 04.10.2019 22:31

Должен ли page.waitForSelector вызываться после page.goto или раньше? Не могли бы вы ответить на аналогичный вопрос, который я задал stackoverflow.com/questions/58909236/…?

Amanda 18.11.2019 08:01

Зачем мне использовать networkidle0, если я могу использовать событие загрузки по умолчанию? Это быстрее использовать networkidle0?

Gary 06.02.2021 10:29

Оберните page.click и page.waitForNavigation в Promise.all

  await Promise.all([
    page.click('#submit_button'),
    page.waitForNavigation({ waitUntil: 'networkidle0' })
  ]);

В некоторых случаях лучшим решением для меня было:

await page.goto(url, { waitUntil: 'domcontentloaded' });

Вот некоторые другие варианты, которые вы можете попробовать:

await page.goto(url, { waitUntil: 'load' });
await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.goto(url, { waitUntil: 'networkidle0' });
await page.goto(url, { waitUntil: 'networkidle2' });

Вы можете проверить это в документации кукольника: https://pptr.dev/#?product=Puppeteer&version=v2.1.1&show=api-pagewaitfornavigationoptions

Это не гарантирует, что все загруженные скрипты завершат выполнение. Следовательно, HTML все еще может отображаться, и это будет продолжаться.

AbuZubair 23.11.2020 02:12

В последней версии Puppeteer у меня работал networkidle2:

await page.goto(url, { waitUntil: 'networkidle2' });

Иногда события networkidle не всегда указывают на то, что страница полностью загружена. Все еще может быть несколько JSscripts, изменяющих содержимое на странице. Таким образом, наблюдение за завершением изменений исходного кода HTML браузером, похоже, дает лучшие результаты. Вот функция, которую вы могли бы использовать -

const waitTillHTMLRendered = async (page, timeout = 30000) => {
  const checkDurationMsecs = 1000;
  const maxChecks = timeout / checkDurationMsecs;
  let lastHTMLSize = 0;
  let checkCounts = 1;
  let countStableSizeIterations = 0;
  const minStableSizeIterations = 3;

  while(checkCounts++ <= maxChecks){
    let html = await page.content();
    let currentHTMLSize = html.length; 

    let bodyHTMLSize = await page.evaluate(() => document.body.innerHTML.length);

    console.info('last: ', lastHTMLSize, ' <> curr: ', currentHTMLSize, " body html size: ", bodyHTMLSize);

    if (lastHTMLSize != 0 && currentHTMLSize == lastHTMLSize) 
      countStableSizeIterations++;
    else 
      countStableSizeIterations = 0; //reset the counter

    if (countStableSizeIterations >= minStableSizeIterations) {
      console.info("Page rendered fully..");
      break;
    }

    lastHTMLSize = currentHTMLSize;
    await page.waitFor(checkDurationMsecs);
  }  
};

Вы можете использовать это после вызова функции страницы load / click и перед обработкой содержимого страницы. например

await page.goto(url, {'timeout': 10000, 'waitUntil':'load'});
await waitTillHTMLRendered(page)
const data = await page.content()

Я не уверен, почему этот ответ не получил больше «любви». На самом деле, в большинстве случаев нам просто нужно убедиться, что JavaScript не вмешивается в страницу, прежде чем мы ее очистим. Сетевые события этого не позволяют, и если у вас есть динамически генерируемый контент, не всегда есть что-то, что вы можете надежно выполнить с помощью "waitForSelector / visible: true" на

Jason 14.05.2020 16:35

Спасибо @roberto - кстати, я только что обновил ответ, вы можете использовать его с событием load, а не networkidle2. Думал, с этим будет немного оптимальнее. Я протестировал это на производстве и могу подтвердить, что он тоже хорошо работает!

Anand Mahajan 20.09.2020 18:08

это отличное решение. Спасибо, что поделились!

cuadraman 09.03.2021 23:22

Отличное решение, которое должно быть частью библиотеки puppeteer, однако, пожалуйста, не ждите, потому что эта функция устарела и будет удалена в следующем выпуске: github.com/puppeteer/puppeteer/issues/6214

Michael Paccione 03.04.2021 01:48

Я попытался установить checkDurationMsecs на 200 мс, и bodyHTMLSize продолжает меняться и давать огромные числа, я также использую электрон и прямоугольник, очень странно.

Ambroise Rabier 29.04.2021 15:15

Хорошо, я нашел эту нелепую ошибку, которую трудно поймать. Если вам удастся поймать эту страницу html длиной 100k, вы поймете, что существуют классы CSS, такие как CodeMirror, должны быть codemirror.net, что означает .... document.body.innerHTML также ловит консоль разработчика! Просто удалите mainWindow.webContents.openDevTools(); для тестирования e2e. Надеюсь, неприятных сюрпризов больше не будет.

Ambroise Rabier 29.04.2021 15:41

Решил мою головную боль при подключении с высокой задержкой .. Хорошо, сделано

Mark Cupitt 08.06.2021 04:41

Отличное решение !!! Это должен быть принятый ответ.

Nenad Milosavljevic 21.08.2021 04:34

Я пробовал несколько решений, это единственное, что действительно сработало полностью. Спасибо, @AnandMahajan

Thiago Pires 14.09.2021 21:07

Вы также можете использовать, чтобы убедиться, что все элементы отрисованы

await page.waitFor('*')

Ссылка: https://github.com/puppeteer/puppeteer/issues/1875

waitFor устарел и будет удален в следующем выпуске. См. github.com/puppeteer/puppeteer/issues/6214 для подробностей и того, как перенести ваш код.
kenberkeley 17.12.2020 00:59

Что касается декабря 2020 года, функция waitFor устарела, поскольку предупреждение внутри кода сообщает:

waitFor is deprecated and will be removed in a future release. See https://github.com/puppeteer/puppeteer/issues/6214 for details and how to migrate your code.

Вы можете использовать:

sleep(millisecondsCount) {
    if (!millisecondsCount) {
        return;
    }
    return new Promise(resolve => setTimeout(resolve, millisecondsCount)).catch();
}

И используйте это:

(async () => {
    await sleep(1000);
})();

просто используйте page.waitForTimeout (1000)

Viacheslav Dobromyslov 10.12.2020 16:41

Проверим. Спасибо.

Or Assayag 10.12.2020 16:44

В выпуске github говорится, что они просто устарели "волшебной" функции waitFor. Вы по-прежнему можете использовать одну из специальных функций waitFor * (). Следовательно, ваш код sleep () не нужен. (Не говоря уже о том, что он слишком сложен для того, что он делает, и, как правило, решать проблемы параллелизма с помощью программных тайм-аутов - плохая идея.)

lxg 20.12.2020 14:20

Я столкнулся с той же проблемой с networkidle, когда работал над внеэкранным рендерером. Мне нужен был движок на основе WebGL, чтобы закончить рендеринг и только потом сделать снимок экрана. Для меня сработал метод page.waitForFunction (). В моем случае использование было следующим:

await page.goto(url);
await page.waitForFunction("renderingCompleted === true")
const imageBuffer = await page.screenshot({});

В коде рендеринга я просто установил для переменной renderingCompleted значение true, когда закончил. Если у вас нет доступа к коду страницы, вы можете использовать другой существующий идентификатор.

Другие вопросы по теме