October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk7 min

How to Compare Website Screenshots with Python and Selenium

Use Selenium to capture matching website states, then compare the PNGs with Pillow to locate visual changes and assess them in context.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture the before-and-after page states with Selenium under matching conditions, then use Pillow’s ImageChops.difference to create a pixel-level diff. Inspect that image to see where rendering changed; a summary score can help triage, but it cannot tell you whether a change is a defect.

What you need for a reliable comparison

The comparison is only meaningful when the reference and candidate screenshots represent the same page state. Keep the route, viewport, scroll position, and interaction state consistent. Where practical, also keep the browser version, operating system, device scale, zoom, locale, color settings, authentication, and page data stable.

  • Python with Selenium and Pillow installed in the environment running your test.
  • A browser and compatible WebDriver, or a Selenium remote WebDriver endpoint.
  • A stable test page and a defined condition that indicates when its content is ready to capture.
  • Two PNG captures with matching dimensions and compatible color modes.

Selenium’s Python API includes window screenshot methods such as save_screenshot, get_screenshot_as_file, and get_screenshot_as_png, as well as set_window_size(width, height). See the Selenium WebDriver API. Pillow’s ImageChops.difference returns the absolute pixel-by-pixel difference image; its channel operations are primarily implemented for 8-bit modes such as L and RGB. See Pillow ImageChops.

Capture matching reference and candidate screenshots

The example below assumes a local Chrome WebDriver is available, a test page at the sample URL, and a page element whose presence means the content under test is ready. Replace the URL and readiness selector with your own. The code captures the reference and candidate in separate browser sessions, explicitly sets the window dimensions, and writes PNG files.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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

URL = "https://example.com"
READY_SELECTOR = "main"
WIDTH = 1365
HEIGHT = 900


def capture(output_path: str) -> None:
    options = webdriver.ChromeOptions()
    # Add the same options and profile-independent settings to both runs.
    driver = webdriver.Chrome(options=options)
    try:
        driver.set_window_size(WIDTH, HEIGHT)
        driver.get(URL)
        WebDriverWait(driver, 20).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, READY_SELECTOR))
        )
        # If the test depends on fonts, images, animation, or client-side data,
        # wait for those specific conditions before taking the screenshot.
        if not driver.save_screenshot(output_path):
            raise RuntimeError(f"Could not save screenshot: {output_path}")
    finally:
        driver.quit()


Path("artifacts").mkdir(exist_ok=True)
capture("artifacts/reference.png")
# After the code/content change, run the candidate capture against the same URL
# and test data, saving it as artifacts/candidate.png.

For a real regression test, preserve the reference image as a baseline artifact rather than regenerating it during the same run as the candidate. Otherwise, a changed page could silently become its own baseline. Keep the capture setup identical between the baseline and candidate runs.

Wait for the state being tested

Waiting for a selector only establishes that the element exists. It does not prove that its text, images, fonts, or asynchronous data have finished rendering. Add a wait that reflects the page condition your test cares about—for example, a particular result appearing, a loading indicator disappearing, or a known state becoming visible. Avoid relying on a short arbitrary sleep when a meaningful readiness condition is available.

Capture an element instead of the whole viewport

For a focused component check, Selenium’s element API can save a web element screenshot to PNG or return its bytes. This reduces unrelated page changes in the image, but the element’s size and surrounding layout still need to be stable. Consult the Selenium WebElement API for the installed Selenium release.

Generate and inspect a pixel-difference image

Compare the two saved images only after checking their dimensions and converting them deliberately to the same mode. The script below saves a visual diff and reports the per-band mean of the difference image.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from PIL import Image, ImageChops, ImageStat

reference_path = "artifacts/reference.png"
candidate_path = "artifacts/candidate.png"
diff_path = "artifacts/diff.png"

with Image.open(reference_path) as ref_source, Image.open(candidate_path) as cand_source:
    reference = ref_source.convert("RGB")
    candidate = cand_source.convert("RGB")

    if reference.size != candidate.size:
        raise ValueError(
            f"Screenshot sizes differ: {reference.size} vs {candidate.size}. "
            "Capture both with the same viewport and page state."
        )

    difference = ImageChops.difference(reference, candidate)
    difference.save(diff_path)

    means = ImageStat.Stat(difference).mean
    print(f"Saved visual diff to {diff_path}")
    print(f"Mean absolute channel differences (R, G, B): {means}")

Black areas in the diff indicate matching pixels; non-black areas show pixel changes. The image is usually the most useful first result because it makes the location and shape of changes visible. Pillow documents ImageStat.Stat statistics for global images and masked regions; its mean property is the average arithmetic pixel level for each band. See Pillow ImageStat.

The channel means are a triage aid, not a universal pass/fail score. A small changed region can be important even when the whole-page mean is low, while harmless anti-aliasing or dynamic content can increase the mean. Set any threshold around the needs of your page and retain the diff image for review.

Choose a comparison scope and handle dynamic regions

Choice Use it when Trade-off
Browser viewport capture You need to check page layout at a particular viewport and scroll position. Changes anywhere in the captured viewport can appear in the diff.
Element capture You want to focus on a component or region. The element and its layout context must still render consistently.
Visual diff image You need to locate and diagnose changed pixels. It identifies change, not its cause or significance.
Summary statistic or threshold You need a compact signal for test triage. A global summary can conceal a localized defect; define thresholds for your own project.
One stable page state You are establishing a baseline for a critical user path. It does not cover other routes, viewports, or interaction states.
Multiple routes or states You need broader coverage of user-critical paths. More baselines and conditions need to be maintained.

Dynamic content such as timestamps, ads, rotating modules, or randomized identifiers can create noise. Prefer deterministic test data or narrowly mask regions only when those areas are outside the purpose of the test. Do not exclude a region simply because it contains an unexpected result: that can hide the very regression you intended to catch.

Troubleshoot common comparison failures

  • The screenshots have different dimensions: Set the same Selenium window dimensions before navigation and verify the resulting image sizes. Browser chrome, scaling, or capture scope can affect output dimensions; compare the files rather than assuming the requested window size guarantees identical image bounds.
  • Fonts or images look incomplete: Wait for the specific font, image, or content condition that matters to the test before capture. Element presence alone may be too early.
  • The diff is noisy between runs: Stabilize test data and rendering conditions, including browser, operating system, device scale, locale, zoom, and page state. Mask only well-defined, irrelevant regions.
  • The image modes differ or Pillow rejects the operation: Convert both images deliberately to the same compatible mode, such as RGB, and confirm they have identical dimensions before calling ImageChops.difference.
  • A low mean hides a visible problem: Inspect the diff image and consider regional statistics or focused element captures. A single whole-image mean may dilute a small but consequential change.
  • The screenshot file was not written: Check the boolean result from save_screenshot, the destination directory and permissions, and whether the driver is still active when the capture call runs.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability, and cost considerations

Each baseline/candidate comparison requires browser capture work plus image comparison. Keep the number of routes, viewport sizes, and interaction states aligned with the user-critical coverage you need; expanding coverage also increases the baselines and conditions to maintain. Reuse deterministic test data where possible, and save screenshots and diff artifacts with the test run so a failure can be investigated. This Selenium-and-Pillow workflow runs in your own test environment; its runtime and infrastructure cost depend on your browser setup and test suite.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Or skip the browser setup

If you would rather request a screenshot than manage browser capture code, ScreenshotNeo provides a website screenshot API and MCP server. One GET request can return an image or PDF; its API documentation covers request options.

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

ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and responses identify page verdict and billing status in headers. Its MCP server offers take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. For screenshot comparison, you would still download and compare the resulting images with a diff workflow such as the Pillow method above.

Sign up for 1,000 free screenshots a month with no card.

Frequently Asked Questions

Can a pixel-difference image tell me whether a visual change is a bug?

No. It shows where pixels differ; review the changed area and judge it against the expected page behavior.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Which Selenium screenshot method should I use for a viewport PNG?

Use save_screenshot or get_screenshot_as_file for a file, or get_screenshot_as_png when you need PNG bytes. Check the API reference for your installed Selenium version.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. Shenzhen desk3 min
    HONOR Expands Beyond Smartphones With Humanoid Robot RevealHONOR said it unveiled its first humanoid robot at MWC 2026 and named shopping assistance, workplace inspections, and supportive companionship as intended uses. Later Robotics D1 claims and a reported…
  2. Cupertino desk5 min
    Apple Unveils AirPods Max 2: The Upgrade That Should Have Happened Years AgoAirPods Max 2 adds H2-powered audio features and Apple claims up to 1.5× more effective ANC, but its design, Smart Case, and 20-hour battery rating are unchanged. Wired lossless audio…
  3. Cupertino desk4 min
    Apple’s OLED Touch MacBooks Are Coming—but the Dynamic Island Is the Real GambleApple has not announced an OLED touchscreen MacBook, but reports point to high-end models arriving in late 2026 or early 2027. The reported Mac Dynamic Island could be useful, but…
Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.