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:
- 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.
- Take a screenshot of the viewport
- Scroll the page right by the width the screenshot
- Do this until you have screenshots for the entire width of the page
- Scroll the page down by height of the screenshot and takes screenshots for that row
- 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
driver.manage.window.maximize
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
end
height_captured += screenshot.height
y_tiles += 1
end
# 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