Puppeteer 等待所有图像加载然后截屏

IT技术 javascript node.js google-chrome automation puppeteer
2021-03-02 18:24:11

我正在使用Puppeteer尝试在所有图像加载后截取网站的屏幕截图,但无法使其正常工作。

这是我到目前为止的代码,我使用https://www.digg.com作为示例网站:

const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('https://www.digg.com/');

    await page.setViewport({width: 1640, height: 800});

    await page.evaluate(() => {
        return Promise.resolve(window.scrollTo(0,document.body.scrollHeight));
    });

    await page.waitFor(1000);

    await page.evaluate(() => {
        var images = document.querySelectorAll('img');

        function preLoad() {

            var promises = [];

            function loadImage(img) {
                return new Promise(function(resolve,reject) {
                    if (img.complete) {
                        resolve(img)
                    }
                    img.onload = function() {
                        resolve(img);
                    };
                    img.onerror = function(e) {
                        resolve(img);
                    };
                })
            }

            for (var i = 0; i < images.length; i++)
            {
                promises.push(loadImage(images[i]));
            }

            return Promise.all(promises);
        }

        return preLoad();
    });

    await page.screenshot({path: 'digg.png', fullPage: true});

    browser.close();
})();
5个回答

有一个内置选项

await page.goto('https://www.digg.com/', {"waitUntil" : "networkidle0"});

networkidle0 - 当至少 500 毫秒没有超过 0 个网络连接时,认为导航已完成

networkidle2 - 当至少 500 毫秒没有超过 2 个网络连接时,认为导航已完成。

PS 当然,如果您使用的是像 Twitter 这样的无限滚动单页应用程序,它当然不会起作用。

我认为应该是: { waitUntil: "networkidle" } 而不是 {"waitUntil" : "networkidle"}
2021-04-25 18:24:11
嗨,每次我点击它会加载的东西时,我怎么能等待下一个网络空闲,但是你没有看到任何 goto,因为它是一个按钮点击。
2021-04-25 18:24:11
在digg.com的情况下,某些图像仅在您向下滚动时才加载,您知道滚动后等待图像加载的方法吗?
2021-04-26 18:24:11
我想您的解决方案会起作用,但是 - 在研究了 digg 主页的工作原理之后 - 我会说您必须一点一点地滚动,而在您的代码中,您几乎可以跳过一整页。查看源代码 - 有很多延迟加载的图像只会在视口中加载。
2021-04-26 18:24:11
在最新的puppeteer版本networkidle中,已弃用并替换为 networkidle0& networkidle2 github.com/GoogleChrome/puppeteer/blob/master/docs/...
2021-05-03 18:24:11

另一种选择,实际评估以在加载所有图像时获取回调

此选项也适用于不支持等待选项的setContentnetworkidle0

await page.evaluate(async () => {
  const selectors = Array.from(document.querySelectorAll("img"));
  await Promise.all(selectors.map(img => {
    if (img.complete) return;
    return new Promise((resolve, reject) => {
      img.addEventListener('load', resolve);
      img.addEventListener('error', reject);
    });
  }));
})
@BenjaminGruenbaum 是的,但它是事件发射器,Promise它不会完全相同的 npm?,+感谢您的良好编辑
2021-04-29 18:24:11
EventTarget据我所知,您还不能自动Promises - 但其余的不需要new Promise:)
2021-05-06 18:24:11
仅供参考,此答案已过时。现在setContent支持waitUntil,这很有帮助。
2021-05-07 18:24:11
2021-05-14 18:24:11
请注意,与networkidle不同evaluate是,调用时,将等待基于 DOM 中存在的标签的所有图像因此,如果脚本异步添加更多图像,这将不起作用(理论上您可以递归调用它,但是……嗯)。
2021-05-16 18:24:11

等待延迟加载图像

您可能需要考虑先使用诸如Element.scrollIntoView()延迟加载图像的方法向下滚动

await page.goto('https://www.digg.com/', {
  waitUntil: 'networkidle0', // Wait for all non-lazy loaded images to load
});

await page.evaluate(async () => {
  // Scroll down to bottom of page to activate lazy loading images
  document.body.scrollIntoView(false);

  // Wait for all remaining lazy loading images to load
  await Promise.all(Array.from(document.getElementsByTagName('img'), image => {
    if (image.complete) {
      return;
    }

    return new Promise((resolve, reject) => {
      image.addEventListener('load', resolve);
      image.addEventListener('error', reject);
    });
  }));
});
嗨格兰特,这不会获取所有图像。例如,尝试使用给定的 URLinsight.com/en_US/search.html?qtype=all&q=HP%20Printers
2021-05-19 18:24:11

我面临着完全相同的问题。我有一种感觉,解决方案将涉及使用:

await page.setRequestInterceptionEnabled(true);

page.on('request', interceptedRequest => {
    //some code here that adds this request to ...
    //a list and checks whether all list items have ...
    //been successfully completed!
});

https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagesetrequestinterceptionenabledvalue

应该可以仅使用Promise来实现它。
2021-04-29 18:24:11

我找到了一个适用于使用page.setViewPort(...)方法的多个站点的解决方案,如下所示:

const puppeteer = require('puppeteer');

async(() => {
    const browser = await puppeteer.launch({
        headless: true, // Set to false while development
        defaultViewport: null,
        args: [
            '--no-sandbox',
            '--start-maximized', // Start in maximized state
        ],
    });

    const page = await = browser.newPage();
    await page.goto('https://www.digg.com/', {
        waitUntil: 'networkidle0', timeout: 0
    });

    // Get scroll width and height of the rendered page and set viewport
    const bodyWidth = await page.evaluate(() => document.body.scrollWidth);
    const bodyHeight = await page.evaluate(() => document.body.scrollHeight);
    await page.setViewport({ width: bodyWidth, height: bodyHeight });

    await page.waitFor(1000);
    await page.screenshot({path: 'digg-example.png' });
})();
waitFor已弃用,将在未来版本中删除:有关详细信息,请参阅github.com/puppeteer/puppeteer/issues/6214
2021-04-29 18:24:11