ScreenshotNeo

BlogHow-to

How to Take Website Screenshots for Visual Testing with Selenium

Capture stable Selenium screenshots for visual checks. Learn how to wait for page state, choose capture scope, save artifacts, and handle common failures.

By the ScreenshotNeo team4 October 20269 min read

To take website screenshots for visual testing with Selenium, start a WebDriver session, navigate to the page, wait for the specific application state you want to inspect, then save a driver or element screenshot as an image artifact. Selenium captures the image; a separate review or comparison step decides whether it differs acceptably from a baseline.

The key to useful visual checks is a repeatable capture: use a stable readiness condition and keep the browser environment consistent. A successful navigation does not guarantee that a single-page app has finished rendering its content.

1. Install Selenium and prepare the capture directory

Use the Selenium language binding already used by your test project. The Python example below uses Selenium 4 and writes screenshots to an artifacts directory. Install the binding with:

python -m pip install selenium

Recent Selenium setups can manage browser drivers through Selenium Manager. If your environment manages the browser and driver separately, install compatible versions; Selenium’s Chrome guidance requires Chrome and ChromeDriver major versions to match.

2. Capture a page and element in Python

This runnable script waits until main is visible, saves a screenshot of the current browsing context, then saves a focused screenshot of that element. Replace the URL and selectors with values that identify the page state under test.

from pathlib import Path

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

ARTIFACTS = Path("artifacts")
ARTIFACTS.mkdir(parents=True, exist_ok=True)

options = webdriver.ChromeOptions()
# Uncomment for a headless CI run:
# options.add_argument("--headless=new")
options.add_argument("--window-size=1440,1000")

driver = webdriver.Chrome(options=options)
try:
    driver.get("https://example.com")

    wait = WebDriverWait(driver, 15)
    main = wait.until(
        EC.visibility_of_element_located((By.CSS_SELECTOR, "main"))
    )

    # Capture the current browsing context.
    driver.save_screenshot(str(ARTIFACTS / "example-home.png"))

    # Capture just the selected element.
    main.screenshot(str(ARTIFACTS / "example-main.png"))
finally:
    driver.quit()

The timeout and selector are example choices, not Selenium defaults. Choose a condition that reflects the actual visual precondition: a loading indicator disappearing, a known component becoming visible, or an application-specific state becoming true. If the page has several asynchronous regions, wait for the region your assertion depends on.

3. Choose a readiness strategy

Selenium navigation waits according to a page-load strategy, but page readiness and visual readiness are different. A single-page app can load more content after document.readyState becomes complete. Wait explicitly for the interface you need to capture.

Strategy Navigation waits for What to do before capture
normal (default) The load event / complete readiness Still wait for dynamic application content.
eager DOMContentLoaded / interactive readiness Wait for required images, components, or app state.
none No page-load blocking Use explicit waits before interacting or capturing.

Set the strategy through browser options when the project needs different navigation behavior. For example, in Python:

from selenium import webdriver

options = webdriver.ChromeOptions()
options.page_load_strategy = "eager"  # "normal", "eager", or "none"
driver = webdriver.Chrome(options=options)

Changing this setting changes when navigation returns; it does not make later app content ready. An explicit wait should remain tied to the particular page and state.

4. Choose the capture scope

Current browsing context

Use driver.save_screenshot(path) when the test concerns the page or current browsing context. The exact capture area can depend on browser and driver behavior. Selenium’s JavaScript API describes a best-effort scope: entire page, current window, visible portion of the current frame, then the display containing the browser. Do not assume identical full-page behavior across every binding and driver.

One element

Use element.screenshot(path) when the assertion concerns a component such as a navigation bar, card, or form. Element captures make focused review easier, but they do not show surrounding layout context. Confirm the element is visible and in the intended state before capturing.

Frames and windows

For content inside an iframe, switch into that frame before locating and capturing its element. For another tab or window, switch to the intended window handle first. Otherwise, Selenium captures the current context, which may not be the one the test author intended.

5. Save deterministic artifacts

Use filenames that identify the page and meaningful state, such as checkout-empty-cart-chrome.png. Include browser or viewport details if they help distinguish artifacts. Create the output directory before saving, and make sure the test runner preserves the artifact directory when a job fails.

Selenium’s screenshot APIs expose image data as Base64 in some bindings or provide convenience methods that write a PNG file. In JavaScript, the WebDriver screenshot API returns Base64-encoded image data:

const fs = require('node:fs');
const { Builder, By, until } = require('selenium-webdriver');

(async function capture() {
  const driver = await new Builder().forBrowser('chrome').build();
  try {
    await driver.get('https://example.com');
    const main = await driver.wait(
      until.elementLocated(By.css('main')),
      15000
    );
    await driver.wait(until.elementIsVisible(main), 15000);

    const pageBase64 = await driver.takeScreenshot();
    fs.writeFileSync('example-home.png', Buffer.from(pageBase64, 'base64'));

    const elementBase64 = await main.takeScreenshot();
    fs.writeFileSync('example-main.png', Buffer.from(elementBase64, 'base64'));
  } finally {
    await driver.quit();
  }
})();

Install the JavaScript binding with npm install selenium-webdriver, and ensure a compatible browser and driver are available to the environment.

6. Keep visual comparisons repeatable

A pixel comparison is only useful when the capture inputs are controlled well enough for the project’s needs. Record and keep consistent:

  • Browser name and version, driver version, and operating system or container image.
  • Window size and viewport, plus device scale factor when relevant.
  • Fonts, locale, timezone, and data that can change the rendered page.
  • Application state, test account data, and any animations or rotating content that affect the target.
  • The baseline image version and the comparison policy used by the separate diff or review tool.

Selenium documents capture, not a universal visual-diff algorithm, pixel tolerance, masking rule, or CI report format. Select those separately. If dynamic timestamps or personalized regions vary, decide which areas the comparison should ignore and document that policy.

7. Complete examples in other bindings

Java

The following uses Selenium’s screenshot interface to save a browser screenshot. Add an explicit wait for the application-specific state before the capture in a real test.

import java.io.File;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

public class CaptureScreenshot {
  public static void main(String[] args) {
    ChromeOptions options = new ChromeOptions();
    options.addArguments("--window-size=1440,1000");
    ChromeDriver driver = new ChromeDriver(options);
    try {
      driver.get("https://example.com");
      File image = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
      image.renameTo(new File("example-home.png"));
    } finally {
      driver.quit();
    }
  }
}

C#

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;

var options = new ChromeOptions();
options.AddArgument("--window-size=1440,1000");
using var driver = new ChromeDriver(options);
driver.Navigate().GoToUrl("https://example.com");
var screenshot = ((ITakesScreenshot)driver).GetScreenshot();
screenshot.SaveAsFile("example-home.png");

In both examples, add the project’s explicit readiness wait before capture. The exact wait condition depends on the application under test.

8. cURL, Python, and Node.js with ScreenshotNeo

Selenium is appropriate when the test needs an interactive browser session and application-specific setup. For a URL-to-image capture without managing browser setup, ScreenshotNeo provides a screenshot API and MCP server. Its API accepts one GET request for a PNG, JPEG, WebP, or PDF capture. The service removes known consent banners, newsletter popups, and chat widgets before capture; each cleanup step can be turned off.

cURL

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

Python

import requests

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

Node.js

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

See the ScreenshotNeo documentation for request options and configuration. The API supports full-page capture with lazy images loaded, element capture by CSS selector, viewport and device presets, retina scale, dark mode, custom CSS and JavaScript, click and hide selectors, waits, resource blocking, headers, cookies, user agent, authorization, timezone and geolocation, transparent backgrounds, resizing, caching, signed public image links, asynchronous jobs with signed webhooks, bulk captures up to 100 URLs per call, usage data, and an OpenAPI spec. Selenium-style parameter names used by other screenshot APIs also work to ease switching.

Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers report the page verdict and billing status. The MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots. All features are on every plan.

Sign up for ScreenshotNeo’s free 1,000 screenshots a month, with no card required.

9. Troubleshooting Selenium screenshots

Symptom Likely cause Fix
Screenshot is blank or missing app content Navigation returned before client-side rendering finished. Wait for the target component or app loading state to complete; do not rely on readyState alone.
Element is not found Selector is wrong, content is inside a frame, or the element has not appeared yet. Check the selector, switch to the correct frame, and use an explicit wait.
Element screenshot fails or clips unexpectedly The element is hidden, outside the relevant context, or browser capture behavior differs. Wait until it is visible, scroll it into view if needed, and verify driver-specific behavior.
Chrome session fails to start Browser and driver are incompatible or unavailable in the environment. Install compatible versions; Selenium documents matching Chrome and ChromeDriver major versions.
File is not created Output directory is absent, path is unwritable, or Base64 data was not decoded. Create the directory, check permissions, and decode screenshot data before writing it.
Images differ between runs Browser, viewport, fonts, data, timing, or dynamic content changed. Stabilize those inputs and define explicit masking or tolerance in the comparison process.

10. Performance, reliability, and cost

Screenshot capture adds browser work to a test: session startup, navigation, readiness waits, and file handling all affect elapsed time. Reuse a session when appropriate for the test design, but keep test state isolated enough that earlier steps do not change later screenshots. Capture only the scope the assertion needs when a focused artifact is sufficient.

Reliability comes from explicit waits, cleanup in a finally block, compatible browser and driver versions, and preserving artifacts on failure. A timeout should fail the test with enough context to diagnose the missing visual precondition; increasing it blindly can hide a stalled page.

Selenium itself is open-source browser automation software; the browser infrastructure, CI time, artifact storage, and any separate visual comparison service may have their own costs. The reviewed Selenium documentation does not provide a universal runtime benchmark or image-diff pricing. For ScreenshotNeo’s API, plans range from 1,000 free monthly shots to paid options of $5 for 3,000, $15 for 15,000, $39 for 60,000, $99 for 250,000, and $249 for 1,000,000; yearly billing gives two months free.

Frequently asked questions

How do I take a screenshot with Selenium?

Navigate with WebDriver, wait for the intended visual state, then call the binding’s driver screenshot method or an element screenshot method and save the image.

Does Selenium compare screenshots automatically?

No. Selenium captures the image. A separate image comparison or human review step decides whether a change is acceptable.

Does a driver screenshot always capture the full page?

Do not assume that across all bindings and drivers. Capture scope is browser-dependent; confirm the behavior for the configured driver or use an approach that explicitly supports the scope you need.

Should I use a page screenshot or an element screenshot?

Use a page/context screenshot for layout-level checks and an element screenshot for a focused component check. The target should be visible and in a stable state first.