Keyword: selenium screenshot · Updated October 15, 2026

Selenium Screenshot in Python & Java: Full Page, Element, On-Failure (2026)

Selenium's screenshot API is small: one call for the viewport and one for an element. Everything people search for on top of that (full page in Chrome, screenshots only when a test fails, base64 for an HTML report) needs a bit of code that differs between browsers and test frameworks. Below is that code for Python and Java, built on the patterns in the official Selenium documentation.

Cheat sheet

TaskPythonJava
Viewport to filedriver.save_screenshot("p.png")((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE)
Viewport as bytesdriver.get_screenshot_as_png()getScreenshotAs(OutputType.BYTES)
Viewport as base64driver.get_screenshot_as_base64()getScreenshotAs(OutputType.BASE64)
One elementel.screenshot("el.png")el.getScreenshotAs(OutputType.FILE)
Full page, Firefoxdriver.save_full_page_screenshot("f.png")((FirefoxDriver) driver).getFullPageScreenshotAs(OutputType.FILE)
Full page, Chrome / EdgeCDP Page.captureScreenshotCDP Page.captureScreenshot

With Selenium 4, Selenium Manager downloads the matching browser driver automatically, so the examples below run without a separate chromedriver setup.

Viewport screenshot

Python

from selenium import webdriver options = webdriver.ChromeOptions() options.add_argument("--headless=new") options.add_argument("--window-size=1440,900") driver = webdriver.Chrome(options=options) try: driver.get("https://example.com") driver.save_screenshot("example.png") # returns False if writing fails finally: driver.quit()

Java

import java.nio.file.*; import org.openqa.selenium.*; import org.openqa.selenium.chrome.*; ChromeOptions options = new ChromeOptions(); options.addArguments("--headless=new", "--window-size=1440,900"); WebDriver driver = new ChromeDriver(options); try { driver.get("https://example.com"); Path tmp = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE).toPath(); Files.copy(tmp, Path.of("example.png"), StandardCopyOption.REPLACE_EXISTING); } finally { driver.quit(); }

OutputType.FILE writes to a temporary file that is deleted when the JVM exits, so copy it somewhere permanent. The official example uses Apache Commons FileUtils.copyFile; java.nio.file.Files.copy does the same without an extra dependency.

Element screenshot

# Python from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC card = WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, ".pricing-card.pro")) ) card.screenshot("pro-card.png") // Java WebElement card = new WebDriverWait(driver, Duration.ofSeconds(10)) .until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(".pricing-card.pro"))); Files.copy(card.getScreenshotAs(OutputType.FILE).toPath(), Path.of("pro-card.png"), StandardCopyOption.REPLACE_EXISTING);

The explicit wait matters. find_element returns as soon as the node exists, which can be before it has size or content, and you get an empty or tiny image. Elements taller than the window may be cropped to the visible part, depending on the driver; for those, use one of the full-page methods below.

Full page screenshot

The W3C WebDriver screenshot command captures the viewport only. Full-page capture depends on the browser:

BrowserFull page method
FirefoxBuilt in: save_full_page_screenshot / getFullPageScreenshotAs
Chrome, EdgeChrome DevTools Protocol: Page.captureScreenshot with captureBeyondViewport
SafariNo built-in method; resize the window or stitch scrolled captures
Selenium Grid / remoteAugment the driver first (new Augmenter().augment(driver) in Java); CDP also needs the node's DevTools connection

Full page in Firefox

# Python driver = webdriver.Firefox() driver.get("https://example.com/blog") driver.save_full_page_screenshot("blog-full.png") # also: get_full_page_screenshot_as_png(), get_full_page_screenshot_as_base64() // Java FirefoxDriver driver = new FirefoxDriver(); driver.get("https://example.com/blog"); File full = driver.getFullPageScreenshotAs(OutputType.FILE); Files.copy(full.toPath(), Path.of("blog-full.png"), StandardCopyOption.REPLACE_EXISTING);

These methods exist only on the Firefox driver class. If your code holds a generic WebDriver, cast it (Java) or check driver.name == "firefox" (Python).

Full page in Chrome and Edge (CDP)

Ask DevTools for the content size, then capture beyond the viewport with a clip of that size.

Python

import base64 def chrome_full_page_screenshot(driver, path): metrics = driver.execute_cdp_cmd("Page.getLayoutMetrics", {}) size = metrics["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(path, "wb") as f: f.write(base64.b64decode(shot["data"])) chrome_full_page_screenshot(driver, "blog-full.png")

Java

import java.util.*; @SuppressWarnings("unchecked") static void chromeFullPage(ChromeDriver driver, Path out) throws Exception { Map<String, Object> metrics = driver.executeCdpCommand("Page.getLayoutMetrics", Map.of()); Map<String, Object> size = (Map<String, Object>) metrics.get("cssContentSize"); Map<String, Object> clip = Map.of( "x", 0, "y", 0, "width", size.get("width"), "height", size.get("height"), "scale", 1); Map<String, Object> shot = driver.executeCdpCommand("Page.captureScreenshot", Map.of("format", "png", "captureBeyondViewport", true, "clip", clip)); Files.write(out, Base64.getDecoder().decode((String) shot.get("data"))); }

executeCdpCommand lives on ChromiumDriver, so it works for both ChromeDriver and EdgeDriver. With RemoteWebDriver on a Grid, wrap the driver with new Augmenter().augment(driver) and use the HasCdp interface, or switch that test to Firefox.

If the result is still one screen tall, the page scrolls inside a container instead of the document. Inject CSS first: driver.execute_script("document.head.insertAdjacentHTML('beforeend', '<style>html,body,main{height:auto!important;overflow:visible!important}</style>')"), then capture.

The headless window size trap

Screenshots in CI often look different from local runs because the headless window has a different default size than your desktop browser. The responsive layout switches, and element screenshots get cropped. Always set the size explicitly:

# At startup options.add_argument("--window-size=1440,900") # Or at runtime driver.set_window_size(1440, 900)

A quick fallback for full-page captures on any browser: read document.documentElement.scrollHeight and resize a headless window to that height before a normal screenshot. It works for simple pages, but viewport-based CSS (100vh heroes) will stretch to the new height, so the CDP method gives more faithful results.

Screenshot on test failure

The most common bug in failure screenshots is timing: the hook runs after the fixture or @AfterEach has already called driver.quit(), and the screenshot throws or never appears. Each example below captures before teardown.

pytest

# conftest.py import os import pytest from selenium import webdriver @pytest.fixture def driver(): d = webdriver.Chrome() yield d d.quit() @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): outcome = yield report = outcome.get_result() if report.when == "call" and report.failed: drv = item.funcargs.get("driver") if drv is not None: os.makedirs("screenshots", exist_ok=True) drv.save_screenshot(f"screenshots/{item.name}.png")

The call phase report is created before fixture teardown, so the driver is still alive.

JUnit 5

import org.junit.jupiter.api.extension.*; public class ScreenshotOnFailure implements AfterTestExecutionCallback { @Override public void afterTestExecution(ExtensionContext ctx) throws Exception { if (ctx.getExecutionException().isEmpty()) return; Object test = ctx.getRequiredTestInstance(); if (test instanceof HasDriver t) { byte[] png = ((TakesScreenshot) t.driver()).getScreenshotAs(OutputType.BYTES); Path dir = Files.createDirectories(Path.of("screenshots")); Files.write(dir.resolve(ctx.getDisplayName().replaceAll("\\W+", "_") + ".png"), png); } } } // interface HasDriver { WebDriver driver(); } // @ExtendWith(ScreenshotOnFailure.class) on the test class

AfterTestExecutionCallback runs right after the test method and before @AfterEach, which is why it is a safer choice than TestWatcher when the driver is closed in @AfterEach.

TestNG

public class ScreenshotListener implements ITestListener { @Override public void onTestFailure(ITestResult result) { if (result.getInstance() instanceof HasDriver t) { try { byte[] png = ((TakesScreenshot) t.driver()).getScreenshotAs(OutputType.BYTES); Path dir = Files.createDirectories(Path.of("screenshots")); Files.write(dir.resolve(result.getName() + ".png"), png); } catch (Exception ignored) { } } } }

In TestNG, close the driver in @AfterMethod(alwaysRun = true); the listener's onTestFailure runs before it.

Production tips

  • Embed failures in HTML reports as base64 (get_screenshot_as_base64()). The report stays one file and survives CI artifact clean-up.
  • Add a timestamp and browser name to file names when tests run in parallel, or screenshots overwrite each other.
  • Wait for web fonts before capturing pages you will compare: driver.execute_async_script("document.fonts.ready.then(arguments[0])").
  • Hide cookie banners and chat widgets with a small CSS injection rather than clicking them, so captures stay identical between runs.
  • Keep screenshots for failures only in large suites. Capturing every step multiplies run time and storage.

Screenshots without a browser in your stack

Selenium is built for testing user flows. When the goal is a picture of a URL (link previews, archives, reports, monitoring), running WebDriver, a browser and a Grid for it is heavy. A screenshot API returns the full-page image from one HTTP request, with no browser on your side:

import requests r = requests.get( "https://api.snapshotflow.com/screenshot", headers={"X-Api-Key": API_KEY}, params={ "url": "https://example.com/blog", "full_page": "true", "width": 1440, "block_cookie_banners": "true", "format": "png", }, timeout=60, ) r.raise_for_status() open("blog-full.png", "wb").write(r.content)

Java teams can use the official SnapshotFlow Java SDK instead of raw HTTP.

FAQ

What is the difference between save_screenshot and get_screenshot_as_file?

None in practice. In the Python bindings save_screenshot calls get_screenshot_as_file. Both return False on an IOError instead of raising, so check the return value in scripts.

Can I save a Selenium screenshot as JPEG?

WebDriver returns PNG. Convert it with Pillow in Python (Image.open(io.BytesIO(png)).convert("RGB").save("p.jpg", quality=80)) or ImageIO in Java. The CDP command also accepts "format": "jpeg" and "quality".

How do I take a screenshot in Selenium C#?

Cast to ITakesScreenshot, call GetScreenshot() and SaveAsFile("screenshot.png"). Element screenshots work the same way on an IWebElement.

Why does my element screenshot show the wrong area?

Usually a fixed header overlaps the element after scrolling, or the page uses CSS zoom or a device scale factor that shifts coordinates. Hide sticky headers for the capture and keep zoom at 100%.

Sources and further reading

Full-page screenshots without WebDriver

Get a full-page capture of any URL from one HTTP request, in Python, Java or cURL, with no browser or Grid to maintain. The free tier gives you 300 screenshots, no credit card required.