ScreenshotNeo

BlogHow-to

How to Capture a Full-Page Website Screenshot with Selenium in Python and Chrome

Capture a full-page Chrome screenshot with Selenium and Python using Chrome DevTools Protocol, with runnable code, troubleshooting, and a no-browser-setup option.

By the ScreenshotNeo team4 October 20269 min read

To capture a full-page screenshot in Chrome with Selenium and Python, use Chrome DevTools Protocol (CDP) through Selenium’s driver.execute_cdp_cmd() bridge. Selenium’s driver.save_screenshot() captures the current browser window; by itself, it is not a full-document capture. The example below measures the rendered document, asks Chrome to capture beyond the viewport, and writes the returned PNG bytes to a file. Selenium Chromium WebDriver API, versioned Selenium DevTools API reference.

Runnable Python example: full-page PNG in Chrome

This example assumes a recent Selenium Python package and a Chrome/ChromeDriver combination that supports the CDP command and parameters shown. Selenium Manager can manage the driver in typical local setups. In controlled environments, install and pin compatible Chrome and ChromeDriver versions yourself.

from base64 import b64decode
from pathlib import Path

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait

URL = "https://example.com"
OUTPUT = Path("page-full.png")

options = webdriver.ChromeOptions()
# For a headless server, uncomment this if supported by your Chrome version:
# options.add_argument("--headless=new")

# Set a generous viewport width; the capture height comes from the document.
options.add_argument("--window-size=1440,1000")

driver = webdriver.Chrome(options=options)
try:
    driver.get(URL)
    WebDriverWait(driver, 30).until(
        lambda d: d.execute_script("return document.readyState") == "complete"
    )

    # Images and sections may load only after scrolling. See the lazy-loading
    # section below before relying on this measurement for such pages.
    dimensions = driver.execute_script("""
        const root = document.documentElement;
        const body = document.body;
        return {
            width: Math.max(root.scrollWidth, root.clientWidth,
                            body ? body.scrollWidth : 0),
            height: Math.max(root.scrollHeight, root.clientHeight,
                             body ? body.scrollHeight : 0)
        };
    """)

    result = driver.execute_cdp_cmd("Page.captureScreenshot", {
        "format": "png",
        "captureBeyondViewport": True,
        "fromSurface": True,
        "clip": {
            "x": 0,
            "y": 0,
            "width": dimensions["width"],
            "height": dimensions["height"],
            "scale": 1
        }
    })
    OUTPUT.write_bytes(b64decode(result["data"]))
    print(f"Saved {OUTPUT} ({dimensions['width']}×{dimensions['height']})")
finally:
    driver.quit()

The Selenium bridge sends a CDP command and returns its result. The Page.captureScreenshot command returns PNG data encoded as base64; decoding that data produces the image file. CDP is Chrome-specific, and screenshot behavior can vary with browser version and page characteristics. Check the output dimensions and image content on your target pages.

Why ordinary Selenium screenshots are not full-page

driver.save_screenshot("page.png") and driver.get_screenshot_as_file(...) are convenient for the current window. They are suitable for a viewport image or a visual assertion at a known scroll position, but the screenshot API describes a current-window screenshot, not an automatic capture of the entire document. Selenium API documentation.

For just one component, use Selenium’s element screenshot method instead. That captures the chosen element rather than the whole document. WebElement API documentation.

Prepare dynamic and lazy-loaded pages

Waiting for document.readyState to become complete means the document load event has fired. It does not guarantee that every single-page application request has finished, that a delayed widget has rendered, or that images deferred until scrolling have loaded. Make the wait specific to the page and content you need.

Wait for an important element

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

WebDriverWait(driver, 30).until(
    EC.visibility_of_element_located((By.CSS_SELECTOR, "main article"))
)

Scroll to trigger lazy content

Some pages load images or sections as they approach the viewport. Scroll down in increments, allow the page to respond, and then remeasure its height before capturing. This is a practical page-specific strategy, not a guarantee that all lazy-loading implementations will behave the same way.

import time

height = driver.execute_script(
    "return Math.max(document.documentElement.scrollHeight, "
    "document.body ? document.body.scrollHeight : 0)"
)
step = max(400, driver.execute_script("return window.innerHeight"))
for y in range(0, height, step):
    driver.execute_script("window.scrollTo(0, arguments[0])", y)
    time.sleep(0.15)  # Adjust for the site; prefer explicit waits when possible.

driver.execute_script("window.scrollTo(0, 0)")
WebDriverWait(driver, 10).until(
    lambda d: d.execute_script("return window.scrollY") == 0
)
# Recalculate document dimensions after the scroll pass, then capture.

For production automation, replace fixed sleeps with waits for known content or image completion where the page exposes a reliable condition. If the page continuously appends content as you scroll, use a bounded loop and a maximum height or time limit so the capture job cannot run forever.

CDP capture options and practical choices

Setting or approach What it controls When to use it
format Output image encoding; PNG is the default-oriented choice in this example. Use PNG for lossless text and UI captures. Check the protocol and installed Chrome behavior if selecting JPEG or WebP.
captureBeyondViewport Allows capture beyond the visible viewport in supported CDP versions. Enable it for a document-height clip that extends below the viewport.
clip Defines the rectangle to capture with x/y position, width, height, and scale. Use document dimensions for a full-document image; use a smaller rectangle for a bounded region.
scale Scales the clip in the capture request. Start at 1. Larger values increase pixel dimensions and memory use.
fromSurface Selects capture from the rendered surface in protocol versions that expose it. The sample explicitly requests it; confirm compatibility against the CDP schema associated with your Chrome version.
driver.save_screenshot() Captures the current window. Use for viewport-only captures.
element.screenshot() Captures one WebElement. Use when the target is a component, chart, or individual panel.
Scroll and stitch Captures viewport-sized pieces and joins them in application code. Consider only when a CDP capture is unsuitable; stitching adds complexity and can create seams or repeated sticky UI.

CDP command fields are tied to Chrome’s protocol version. If a parameter is rejected, inspect the protocol supported by the installed browser and adjust the request rather than assuming every Chrome release accepts the same schema. Selenium describes WebDriver BiDi as a cross-browser direction, but the sources here do not establish a universal BiDi full-page screenshot recipe. Selenium WebDriver documentation.

Handle page width, overlays, and unusually long documents

  • Choose the viewport before navigation. Responsive layouts depend on viewport width. Set a window size before loading the page, and check the resulting rendered width if exact layout matters.
  • Sticky headers may appear at an unexpected position. A fixed or sticky element can remain pinned while Chrome captures a tall page. Inspect the image; behavior depends on the page and capture path.
  • Consent dialogs and popups can obscure content. Dismiss them through an authorized, page-specific interaction if appropriate, or capture what the visitor actually sees. Do not assume a screenshot API automatically removes overlays.
  • Very tall images need memory. Pixel dimensions grow with document height and scale. A huge PNG may use substantial browser and Python memory and may be slow to write or inspect.
  • Horizontal overflow needs a decision. The sample measures the maximum scroll width as well as client width. If overflow is accidental, decide whether the desired result is the full overflowing canvas or the visible responsive layout.

cURL, Python, and Node.js alternatives

The Selenium method above is the requested DIY approach. If your pipeline already uses another language to call Chrome, the same conceptual distinction applies: a viewport screenshot is not automatically a full-page capture. For managed screenshot output without operating Chrome and ChromeDriver yourself, ScreenshotNeo provides a website screenshot API and MCP server. See the ScreenshotNeo API documentation.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://example.com \
  -o shot.webp

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));

Or skip the browser setup

ScreenshotNeo returns a screenshot from one GET request. Cookie banners are accepted like a visitor, and more than 60 known consent platforms, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers indicate the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Features are available on every plan. Learn about ScreenshotNeo.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://example.com \
  -o shot.webp

See the API documentation and sign up for 1,000 free screenshots a month, with no card.

Troubleshooting

Symptom Likely cause What to do
save_screenshot() only shows the visible area That method captures the current window. Use the CDP full-page request with a document-sized clip and beyond-viewport support.
unknown command or CDP method error The browser/driver does not support the command as requested, or the command name is wrong. Confirm this is Chrome through Selenium’s Chromium driver, update or align Chrome and driver versions, and check the version-specific protocol schema.
Invalid parameters or clip rejected A field or value differs from the CDP schema supported by the installed Chrome. Check the matching protocol reference; verify clip width and height are positive numbers and remove unsupported optional fields.
Bottom of the page is blank or content is missing Content may load on scroll or after asynchronous application work. Wait for a page-specific condition, scroll to trigger lazy sections, remeasure dimensions, and capture after returning to the intended scroll position.
Screenshot cuts off or is unexpectedly small Measured dimensions may not reflect the content, or the browser may impose practical limits for a very large surface. Log width and height, inspect the page’s scroll dimensions, reduce scale, and consider bounded captures or a scroll-and-stitch workflow.
Sticky header repeats, overlaps, or covers content Fixed-position elements behave differently in a tall capture than in an ordinary viewport. Inspect target-page behavior; if appropriate, hide or adjust the element with a page-specific script before capture, and document that alteration.
Chrome exits unexpectedly in a container Browser startup dependencies, sandbox configuration, or memory may be insufficient. Review the container’s Chrome requirements and logs, provide required runtime libraries, and size memory for the page and output. Avoid blindly adding security-disabling flags.

Performance, reliability, and cost

  • Runtime: Navigation, page-specific waits, lazy-load scrolling, and image encoding usually dominate the job. A scroll pass adds time proportional to the number of viewport steps and any delay between them.
  • Memory: A raster image’s pixel count is width × height × scale². Tall pages and high scale values can consume significant memory. Capture only the dimensions and format you need.
  • Reliability: Pin and record Selenium, Chrome, and ChromeDriver versions in repeatable automation. Use explicit waits tied to required page content, set timeouts, always quit the driver in a finally block, and retain failure logs. CDP’s Chrome-specific interface is a compatibility consideration.
  • Cost: Selenium and Chrome are software components, but running a browser has infrastructure, maintenance, and engineering costs. ScreenshotNeo’s listed monthly plans are Free: 1,000 shots; Starter: $5 for 3,000; Growth: $15 for 15,000; Pro: $39 for 60,000; Scale: $99 for 250,000; and Business: $249 for 1,000,000. Yearly billing gives two months free.

FAQ

Does Selenium have a built-in full-page screenshot method?

The ordinary WebDriver screenshot method captures the current window. For this Chrome guide, CDP provides the beyond-viewport capture path.

Can I use this exact CDP code in Firefox?

No. This implementation uses Chrome DevTools Protocol through Selenium’s Chromium driver. The article does not establish an equivalent universal cross-browser recipe.

Can I save a full-page screenshot as JPEG?

CDP screenshot commands support format selection according to the browser’s protocol. Confirm the supported options for your Chrome version and choose a suitable quality setting if using a lossy format.

How do I capture only a chart or card?

Locate the corresponding WebElement and use its screenshot method; that is an element capture rather than a document capture.

Can an AI agent capture a page without a Selenium script?

ScreenshotNeo’s MCP server includes take_screenshot, get_page_info, and capture_pdf tools for MCP clients such as Claude and Cursor.