Puppeteer 等待页面完全加载

IT技术 javascript pdf-generation puppeteer google-chrome-headless
2021-02-09 01:00:14

我正在从网页创建 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 因为该页面有在计算后呈现的图表和图形。

帮助将不胜感激。

6个回答

您可以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,
});
应该page.waitForSelector在之后page.goto还是之前调用你能回答我问过的类似问题stackoverflow.com/questions/58909236/...吗?
2021-03-21 01:00:14
当我可以使用默认加载事件时,为什么要使用 networkidle0?使用networkidle0是否更快?
2021-04-01 01:00:14
信号“networkidle0”的文档在哪里?
2021-04-02 01:00:14
2021-04-04 01:00:14

有时,networkidle事件并不总是表明页面已完全加载。仍然可能有一些JS scripts修改页面上的内容。因此,观察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.log('last: ', lastHTMLSize, ' <> curr: ', currentHTMLSize, " body html size: ", bodyHTMLSize);

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

    if(countStableSizeIterations >= minStableSizeIterations) {
      console.log("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()
很好的解决方案,应该是 puppeteer 库的一部分,但是请不要等待已被弃用,并将在未来版本中删除:github.com/puppeteer/puppeteer/issues/6214
2021-03-20 01:00:14
我尝试了几种解决方案,这是唯一真正有效的解决方案。谢谢你,@AnandMahajan
2021-03-22 01:00:14
我不确定为什么这个答案没有得到更多的“爱”。实际上,很多时候我们真的只需要确保在我们抓取页面之前 JavaScript 已经完成了对页面的处理。网络事件不会实现这一点,如果您有动态生成的内容,则并不总是可以可靠地执行“waitForSelector/visible:true”
2021-03-29 01:00:14
谢谢@roberto - 顺便说一句,我刚刚更新了答案,您可以将其与 'load' 事件而不是 'networkidle2' 一起使用。认为这样会更好一些。我已经在生产中对此进行了测试,并且可以确认它也运行良好!
2021-03-29 01:00:14
这是一个很好的解决方案。感谢你的分享!
2021-04-11 01:00:14

在某些情况下,对我来说最好的解决方案是:

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' });

您可以在 puppeteer 文档中查看:https ://pptr.dev/# ? product = Puppeteer & version = v11.0.0 & show = api-pagewaitfornavigationoptions

指向文档的链接现在已损坏
2021-03-27 01:00:14
这并不能确保加载的任何脚本都已完成执行。因此 HTML 仍然可以呈现,这将继续进行。
2021-04-11 01:00:14
链接已更新,谢谢@chovy
2021-04-13 01:00:14

我总是喜欢等待selectors,因为它们中的许多是页面已完全加载的一个很好的指标:

await page.waitForSelector('#blue-button');
@Arch4Arts 你应该创建你自己的点击功能来等待你和点击
2021-03-24 01:00:14
你是个天才,这是一个如此明显的解决方案,尤其是当你在等待特定元素时,我没有猜到自己,谢谢!
2021-04-10 01:00:14

page.clickand包裹page.waitForNavigation在 Promise.all 中

  await Promise.all([
    page.click('#submit_button'),
    page.waitForNavigation({ waitUntil: 'networkidle0' })
  ]);
page.waitForNavigation({ waitUntil: 'networkidle0' })这是一样的page .waitForNetworkIdle()吗?
2021-03-17 01:00:14