Take a full page screenshot with Puppeteer

Posted July 20, 2022 by Dmytro Krasun ‐ 3 min read

You can take a full page screenshot with Pupeeter by specifying the fullPage parameter as true when taking a screenshot.

But there is a caveat. If a site has lazy-loaded images, they won't be rendered. Let's examine how to fix the issue and trigger lazy image loading with Puppeteer.

Default full page behavior

Let’s take a regular full page screenshot first:

const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch({ headless: true });
    try {
        const page = await browser.newPage();

        await page.setViewport({ width: 1280, height: 1024 });
        await page.goto('https://apple.com/', { waitUntil: ['load', 'domcontentloaded'] });

        await page.screenshot({ type: 'jpeg', path: 'screenshot.jpeg', fullPage: true });
    } catch (e) {
        console.log(e)
    } finally {
        await browser.close();
    }
})();

The result is:

The Apple website without scroll

As you there is a bunch of images that are not loaded. But there is a solution on how to trigger lazy loading.

Full page and scroll

To trigger lazy loaded images to render, we will scroll the page to the bottom and back before taking a screenshot:

const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch({ headless: true });
    try {
        const page = await browser.newPage();

        await page.setViewport({ width: 1280, height: 1024 });
        await page.goto('https://apple.com/', { waitUntil: ['load', 'domcontentloaded'] });

        const scrollHeight = await scroll(page);
        await page.setViewport({
            width: 1280,
            height: scrollHeight
        });

        await page.screenshot({ type: 'png', path: 'screenshot.png', fullPage: true });
    } catch (e) {
        console.log(e)
    } finally {
        await browser.close();
    }
})();

async function scroll(page) {
    return await page.evaluate(async () => {
        return await new Promise((resolve, reject) => {
            var i = setInterval(() => {
                window.scrollBy(0, window.innerHeight);
                if (document.scrollingElement.scrollTop + window.innerHeight >= document.scrollingElement.scrollHeight) {
                    window.scrollTo(0, 0);
                    clearInterval(i);
                    resolve(document.scrollingElement.scrollHeight);
                }
            }, 100);
        });
    });
}

The result is:

The Apple website after scroll

As you see, I also changed the viewport size since sometimes, after scrolling back, you might have artifacts โ€” I succeeded in removing them by resetting the viewport.

“Infinite” scroll

Detecting infinite scroll with Puppeteer is not easy, but it is possible.

It is out of the scope for the article, but it is hard to find the case when you need to make screenshots with “infinite” scroll sites. And if you need to, you can use the next algorithm:

  1. Load the page, wait until it is loaded.
  2. Scrolling until there the size of the page is not changed.
  3. Take the screenshot.

If you try to do it with Twitter or Instagram for account that has a lot of posts, you absolutely will end up with crashed browser instance due to the memory exhaustion.

Home exercise

Want to improve the algorithm and practice full page screenshot taking? Try to take a screenshot of the Tesla website:

The problem is that you scroll the site’s background, and there is no full page. Try to do it by yourself.

Save time and outsource

If you have time and have already installed Puppeteer, you can easily copy and use the code I provided. But if you donโ€™t want to deal with Puppeteer, you can use ScreenshotOne.com API to save time.

Why does it save time? Because I fix bugs, update libraries to the latest version, test them and adapt full page rendering to new sites. If you have a limited set of sites, I would not use API and would go with Puppeteer.

Additional resources

Have a nice day ๐Ÿ‘‹ and you also might find helpful: