Fullpage screenshots with Selenium Webdriver in Chrome and Safari

Saber Feedback makes use of Selenium to create accurate screenshots in over 500 different browser / operating system combinations. The problem I (and many Selenium users) face is that the drivers for selenium are not consistent in how they take screenshots. Most browser drivers in Selenium take a screenshot of the entire webpage, but version 2 of the Chrome driver, as well as the Safari driver only take screenshots of the viewport.

Most people suggest implementing a ‘scroll and stitch’ method to overcome this issue in Chrome and Safari, but this won’t always create accurate screenshots, eg: if the site has elements with css position: fixed; those elements may end up in the screenshots multiple times, in different places. This issue can be overcome in many cases by changing these elements to position: absolute;, but this method falls down if elements are positioned relative to the scroll position using javascript.

The solution I ended up using for Saber is modified version of ‘scroll and stitch’, making use of iframes to avoid scrolling the page:

  1. Instead of navigating to the page you’d like to screenshot, navigate to a page containing nothing but an iframe. The iframe contains the desired screenshot page, and must be sized to contain the entire page without scrolling.
  2. Take a screenshot of the viewport
  3. Scroll the page right by the width the screenshot
  4. Do this until you have screenshots for the entire width of the page
  5. Scroll the page down by height of the screenshot and takes screenshots for that row
  6. Stitch the screenshots together

Here’s the sample code, it’s in Ruby, but can easily be ported to your favourite language

# needs the selenium-webdriver and mini_magick gems

# Substitute these with the size of your web page
page_width = 2000
page_height = 4000

height_captured = 0
y_tiles = 0

driver = Selenium::WebDriver # Connect to your Selenium Webdriver
driver.navigate.to iframe_url 

while height_captured < page_height  
  width_captured = 0
  x_tiles = 0
  while width_captured < page_width
    tile_file = "screenshot-#{y_tiles}-#{x_tiles}.png"

    driver.execute_script "window.scrollTo(#{width_captured}px, #{height_captured});"

    driver.save_screenshot tile_file

    screenshot = MiniMagick::Image.new tile_file
    width_captured += screenshot.width
    x_tiles += 1
  height_captured += screenshot.height
  y_tiles += 1

# Use imagemagick's montage command to stitch the screenshot tiles together
`montage screenshot-[0-#(y_tiles}]-[0-#{x_tiles}].png -tile #{x_tiles}x#{y_tiles} -geometry #{screenshot.width}x#{screenshot.height}+0+0 screenshot.png`

This method works with both version 2 of Chrome Driver, and all versions of Safari Driver - Chrome Driver version 1 already takes full page screenshots.

- Matt Bearman