【问题标题】:Puppeteer wait for all images to load then take screenshotPuppeteer 等待所有图像加载然后截图
【发布时间】:2018-02-20 00:05:46
【问题描述】:

我正在使用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();
})();

【问题讨论】:

    标签: javascript node.js google-chrome automation puppeteer


    【解决方案1】:

    为此有一个built-in option

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

    networkidle0 - 在至少 500 毫秒内没有超过 0 个网络连接时,考虑完成导航

    networkidle2 - 在至少 500 毫秒内没有超过 2 个网络连接时考虑完成导航。

    附:当然,如果您使用的是 Twitter 等无限滚动单页应用程序,则它不会工作。

    【讨论】:

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

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

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

    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 是的,但它是事件发射器,npm 承诺它不会完全一样?,+感谢编辑
    • 据我所知,您还不能自动承诺EventTargets - 但其余的不需要new Promise :)
    • 请注意,与networkidle 不同,当调用evaluate 时,它将等待基于DOM 中存在的标签的所有图像。因此,如果脚本异步添加更多图像,这将不起作用(理论上您可以递归调用它,但是......嗯)。
    • 仅供参考,此答案已过时。 setContent 现在支持waitUntil,非常有用。
    【解决方案3】:

    等待延迟加载图片

    您可能需要考虑先使用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);
        });
      }));
    });
    

    【讨论】:

    【解决方案4】:

    我面临着完全相同的问题。 我觉得解决方案将涉及使用:

    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来实现。
    【解决方案5】:

    我使用 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' });
    })();
    

    【讨论】:

    猜你喜欢
    • 2019-06-11
    • 1970-01-01
    • 2018-05-07
    • 2022-07-27
    • 1970-01-01
    • 1970-01-01
    • 2012-11-05
    • 2019-12-08
    • 1970-01-01
    相关资源
    最近更新 更多