puppeteer: fullPage screenshot duplicates page (doubles/tripples page length)

So I’m having a weird problem with certain websites that I am trying to screenshoot. Essentially the page is shot and then replicated a number of times down in the png to make a really long screenshot that contains all these replications. Its like somebody copy pasted the page a couple of times onto the bottom of the original.

I have not been able to figure out which sites cause it, but the example below is an example- I can supply more if needed.

Steps to reproduce

Tell us about your environment:

What steps will reproduce the problem?

Sample code with fullPage=True, i.e:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://bonobos.com/shop/tops');
  await page.screenshot({path: 'example.png', fullPage: true});

  await browser.close();
})();

What is the expected result? A pull page screenshot- not just the visible part

What happens instead? A very long image that contains multiple copies of the full page screenshot.

example Don’t think the example will be visible, but worth a shot.

About this issue

  • Original URL
  • State: open
  • Created 7 years ago
  • Reactions: 18
  • Comments: 34 (1 by maintainers)

Most upvoted comments

It seems that with {deviceScaleFactor: 0} it actually works as expected.

Add await page.setViewport({ width: 1000, height: 600, deviceScaleFactor: 0 }); after const browser = await puppeteer.launch(); const page = await browser.newPage(); This solution works for pages with height up to approx. 8700px

Because the issue haven’t been resolved yet, I suggest you to use Playwright instead. It uses very similar syntax (most of the functions are named the same) and fullPage screenshot works like a charm without webpage height restrictions.

@aslushnikov Is there a plan to fix it?

@jiajunli

 const dpr = page.viewport().deviceScaleFactor || 1;
  const maxScreenshotHeight = Math.floor( bugMaxHeight / dpr );
  const imgArr = [];
  // 小于16 * 1024像素高的图片直接截图
  if ( contentSize.height < maxScreenshotHeight ) {
    // 防止意外发生未关闭标签页造成内存爆炸
    let timeoutID = setTimeout( () => page.close(), 2e4 )

    return page.screenshot( {
      fullPage: true
    } ).then( buffer => ( clearTimeout( timeoutID ), page.close(), buffer ) );
  }
  // 大于16 * 1024高度的图片循环截图 放在系统提供的缓存里
  for ( let ypos = 0; ypos < contentSize.height; ypos += maxScreenshotHeight ) {
    const height = Math.min( contentSize.height - ypos, maxScreenshotHeight );
    const tmpName = tmp.tmpNameSync();
    fs.writeFileSync( tmpName, await page.screenshot( {
      clip: {
        x: 0,
        y: ypos,
        width: contentSize.width,
        height
      }
    } ) )
    imgArr.push( tmpName )
  }
  return new Promise( ( resolve, reject ) => {
    // 使用gm这个包进行拼接
    gm( imgArr.shift() )
      .append( imgArr )
      .toBuffer( ( err, buffer ) => {
        page.close()
        err ? reject( err ) : resolve( buffer )
      } );
  } ).catch( () => page.close() );

When can you fix this problem, please

I’ve just added details about this bug to the upstream chromium bug: https://bugs.chromium.org/p/chromium/issues/detail?id=770769#c12 #359 is very related, where you can’t get screenshots taller than 16384px aka (16*1024)px

Folks are correct that dpr/deviceScaleFactor is involved. 😃
Your content will repeat every (16 * 1024) / deviceScaleFactor pixels.

This is ultimately a Chromium compositor bug. It cannot capture a texture larger than 16384px/dpr. So you’ll need a workaround like taking smaller screenshots and stitching together.

I just found and tried out https://github.com/morteza-fsh/puppeteer-full-page-screenshot and had some success. (And put up a PR for a bug I ran into). The implementation seems straightforward enough.

I had the same issue.

Page is about 3000 pixels height, viewport was just less than 1000 pixels in height. The screenshot was the visible part of the page, repeated 3 times (and a little bit).

defaultViewport: null as a launch option resolved this for me.

The resolution of these situations is split into pieces with height less than 16000px ,then merge