Selenium screenshots in Python: full page, element & PDF

Selenium's save_screenshot() only captures the visible viewport, and in Chrome there's no built-in full-page option. Here is tested Selenium 4 code for every common case, the fixes for broken output, and when an HTTP API is simpler.

Basic screenshot

Install with pip install selenium. Selenium 4.6+ downloads a matching chromedriver by itself (Selenium Manager), so you don't need to install the driver.

from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--window-size=1280,800")
driver = webdriver.Chrome(options=options)
driver.get("https://example.com")
driver.save_screenshot("viewport.png")
driver.quit()

This is the viewport only. Note that --window-size sets the window, so the image can come out shorter than 800px. The fix is below.

Full-page screenshot in Chrome (CDP)

Chrome's WebDriver has no full-page method, but Selenium can talk to the DevTools protocol directly. Ask for the full content size and capture beyond the viewport:

import base64
from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--window-size=1280,800")    # headless defaults to 800x600
driver = webdriver.Chrome(options=options)
driver.get("https://en.wikipedia.org/wiki/Web_scraping")

size = driver.execute_cdp_cmd("Page.getLayoutMetrics", {})["cssContentSize"]
shot = driver.execute_cdp_cmd("Page.captureScreenshot", {
    "format": "png",
    "captureBeyondViewport": True,
    "clip": {"x": 0, "y": 0, "width": size["width"], "height": size["height"], "scale": 1},
})
with open("full.png", "wb") as f:
    f.write(base64.b64decode(shot["data"]))
driver.quit()

This works with Chrome and Edge (execute_cdp_cmd is Chromium-only). The old trick of resizing the window to document.body.scrollHeight also works, but it breaks pages that size sections to 100vh, which turn huge.

Full-page screenshot in Firefox

geckodriver supports full-page capture natively: driver.save_full_page_screenshot("full.png") on a webdriver.Firefox() driver. It's Firefox-only, which is why Chrome needs the CDP route above.

Screenshot one element

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

heading = WebDriverWait(driver, 10).until(
    EC.visibility_of_element_located((By.CSS_SELECTOR, "h1")))
heading.screenshot("heading.png")

Wait for the element to be visible first. Calling find_element straight after get() races with client-side rendering on JavaScript-heavy sites.

PDF of a page

import base64
from selenium.webdriver.common.print_page_options import PrintOptions

opts = PrintOptions()
opts.page_width, opts.page_height = 21.0, 29.7     # A4, in cm (the default is US Letter)
opts.margin_top = opts.margin_bottom = 1.0
opts.margin_left = opts.margin_right = 1.0
opts.background = True                              # keep background colours and images
pdf = driver.print_page(opts)
with open("page.pdf", "wb") as f:
    f.write(base64.b64decode(pdf))

print_page() works in headless Chrome and Firefox and uses the page's print CSS (@media print).

Why the screenshot looks wrong (and the fixes)

The same thing as one HTTP call

If screenshots are a side feature, you may not want Chrome, a driver and fonts in your deployment. Shotmint runs the browser for you, scrolls for lazy images and handles full-page capture:

# shot.py, run with: SHOTMINT_KEY=... python3 shot.py
import os
import requests

r = requests.get("https://shotmint.creasework.com/v1/screenshot", params={
    "url": "https://en.wikipedia.org/wiki/Web_scraping",
    "full_page": "true",
    "device_scale_factor": "2",
    "format": "jpeg",
    "quality": "80",
    "block_ads": "true",              # also hides common cookie banners
}, headers={"X-API-Key": os.environ["SHOTMINT_KEY"]}, timeout=90)
r.raise_for_status()
with open("full.jpg", "wb") as f:
    f.write(r.content)
SeleniumShotmint
save_screenshot()GET /v1/screenshot?url=…
CDP captureBeyondViewportfull_page=true (scrolls first, so lazy images load)
element.screenshot()selector=h1
setDeviceMetricsOverridewidth, height, device_scale_factor, mobile=true
Injected display: none CSShide_selectors=.banner, or block_ads=true
print_page(PrintOptions())/v1/pdf with format=A4, print_background, margin

When to keep Selenium

Keep it for browser automation and testing: logging in, clicking through flows, filling forms, cross-browser checks. The API is for when you just need the picture or the PDF of a URL or some HTML. Other languages: Python screenshot API guide, Playwright screenshots. No code at all: the full-page screenshot tool.

Try it with 100 free renders

No signup, no card. Get a key in one click, then buy credits only if you need more (from €5 for 2,000, never expire).

Get a free API key Full API reference