ScreenshotNeo

BlogHow-to

How to Set the Screenshot Image Format in Selenium

Selenium screenshots are PNG by default. Learn how to convert them to JPEG or WebP, preserve transparency, troubleshoot errors, and automate the workflow.

By the ScreenshotNeo team29 September 202611 min read

How to Set the Screenshot Image Format in Selenium

Direct answer: Selenium’s standard WebDriver screenshot methods produce PNG images. There is no format argument that makes save_screenshot() or get_screenshot_as_png() return JPEG or WebP. Capture the screenshot as PNG, then encode those bytes with an image library such as Pillow.

Changing screenshot.png to screenshot.jpg does not convert the file. The bytes remain PNG, and applications that inspect the file signature or MIME type can reject it. The reliable workflow is:

  1. Open the page with Selenium.
  2. Capture PNG bytes or save a PNG file.
  3. Convert the image to JPEG or WebP with an image encoder.
  4. Flatten transparency before writing JPEG, because JPEG has no alpha channel.
  5. Check the output MIME type and dimensions before sending it to downstream systems.

What Selenium actually supports

The Selenium Python API describes get_screenshot_as_file() and save_screenshot() as saving the current window to a PNG image file. get_screenshot_as_png() returns PNG bytes, and get_screenshot_as_base64() returns a base64-encoded PNG. See the Selenium WebDriver Python API.

Method Result Use it when
save_screenshot(path) Writes PNG bytes to a file You only need a PNG artifact
get_screenshot_as_file(path) Writes PNG bytes to a file You want Selenium’s boolean success result
get_screenshot_as_png() PNG bytes in memory You will convert, upload, or process the image
get_screenshot_as_base64() Base64 PNG string An API requires base64 input

Selenium’s element screenshot methods follow the same PNG-oriented behavior. A filename ending in .jpg is not a request to change the encoder. It is only a filename, and Selenium still obtains PNG data.

Complete Python example: Selenium PNG to JPEG

Install the dependencies:

Selenium emits PNG data; an image encoder creates JPEG or WebP afterward.
Selenium emits PNG data; an image encoder creates JPEG or WebP afterward.
python -m pip install selenium pillow

The following script captures a page, converts the PNG bytes to an RGB JPEG, and writes the result:

from io import BytesIO
from pathlib import Path

from PIL import Image
from selenium import webdriver
from selenium.webdriver.chrome.options import Options


def png_bytes_to_jpeg(png_bytes: bytes, output_path: str, quality: int = 90) -> None:
    """Convert PNG bytes to a JPEG file, flattening transparency onto white."""
    with Image.open(BytesIO(png_bytes)) as source:
        image = source

        # JPEG cannot store an alpha channel. Composite transparent pixels
        # against white before converting to RGB.
        if image.mode in ("RGBA", "LA", "P"):
            rgba = image.convert("RGBA")
            background = Image.new("RGB", rgba.size, "white")
            background.paste(rgba, mask=rgba.getchannel("A"))
            image = background
        elif image.mode != "RGB":
            image = image.convert("RGB")

        image.save(output_path, "JPEG", quality=quality, optimize=True)


options = Options()
# Remove this argument when you want to watch the browser.
options.add_argument("--headless=new")
options.add_argument("--window-size=1440,1000")

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

    png_bytes = driver.get_screenshot_as_png()
    png_path = Path("screenshot.png")
    jpg_path = Path("screenshot.jpg")
    png_path.write_bytes(png_bytes)
    png_bytes_to_jpeg(png_bytes, str(jpg_path), quality=90)

    print(f"Wrote {png_path} and {jpg_path}")
finally:
    driver.quit()

The call to convert("RGB") handles screenshots that already have no transparency. The explicit compositing path preserves the visible appearance of transparent pixels by placing them over a white background. Use another background color if your page design requires it.

Saving directly to PNG

If PNG is the correct output, no conversion is needed:

from selenium import webdriver

with webdriver.Chrome() as driver:
    driver.get("https://example.com")
    ok = driver.save_screenshot("screenshot.png")
    if not ok:
        raise RuntimeError("Selenium could not save the screenshot")

Keep PNG when you need lossless pixels, sharp text, transparent areas, or an image that will be edited again. PNG files are often larger than JPEG for photographic pages, but repeated JPEG encoding can introduce visible artifacts around text and icons.

Converting to JPEG correctly

JPEG quality and color mode

Pillow’s image format documentation shows explicit format selection with image.save(..., "JPEG"). JPEG encoders require an RGB or grayscale image. RGBA, LA, and palette images must be converted first. A quality value around 85–92 is a practical starting point; inspect your own pages because text-heavy screenshots can need a higher value.

from io import BytesIO
from PIL import Image

with Image.open(BytesIO(png_bytes)) as image:
    rgb = image.convert("RGB")
    rgb.save("screenshot.jpg", format="JPEG", quality=90, optimize=True, progressive=True)

optimize=True asks Pillow to optimize JPEG tables. progressive=True can help browsers display a large image incrementally, but some strict consumers prefer baseline JPEG. Omit it when compatibility with an older decoder matters.

Why transparency disappears

JPEG has no alpha channel. If you call image.convert("RGB") on an RGBA screenshot, transparent pixels are composited according to the conversion rules and may become black or otherwise unexpected. Composite explicitly onto the intended background:

from PIL import Image

rgba = image.convert("RGBA")
background = Image.new("RGB", rgba.size, (255, 255, 255))
background.paste(rgba, mask=rgba.getchannel("A"))
background.save("screenshot.jpg", "JPEG", quality=90)

If transparency is a requirement, keep the PNG or use WebP with alpha instead of JPEG.

Converting to WebP

WebP can be lossy or lossless and can retain transparency. Pillow supports explicit WebP encoding when the WebP feature is available in your installation:

from io import BytesIO
from PIL import Image

with Image.open(BytesIO(png_bytes)) as image:
    image.save("screenshot.webp", format="WEBP", quality=85, method=6)

# Lossless WebP, including alpha when present:
with Image.open(BytesIO(png_bytes)) as image:
    image.save("screenshot-lossless.webp", format="WEBP", lossless=True, method=6)

Check support before relying on WebP in a deployment image:

from PIL import features

if not features.check("webp"):
    raise RuntimeError("This Pillow build does not include WebP support")

Element screenshots and full-page captures

Capture one element, then convert

Element screenshots are also PNG-oriented. Locate the element, capture it, and pass the returned bytes through the same conversion function:

from io import BytesIO
from PIL import Image
from selenium import webdriver

with webdriver.Chrome() as driver:
    driver.get("https://example.com")
    element = driver.find_element("css selector", "main")
    png_bytes = element.screenshot_as_png

    with Image.open(BytesIO(png_bytes)) as image:
        image.convert("RGB").save("main.jpg", "JPEG", quality=90)

Wait for the element to be present and visible when the page renders asynchronously. A selector that exists in the DOM can still have zero size, be covered by a modal, or contain unloaded images.

Full-page screenshots

Setting a large window size is not identical to a true full-page capture. Browser and driver versions differ in how they handle the document’s scroll height, fixed headers, and lazy-loaded content. A common approach is to read the document dimensions, resize the window, and capture:

from selenium import webdriver

with webdriver.Chrome() as driver:
    driver.get("https://example.com")
    width = driver.execute_script("return document.documentElement.scrollWidth")
    height = driver.execute_script("return document.documentElement.scrollHeight")
    driver.set_window_size(width, height)
    driver.save_screenshot("full-page.png")

For pages that lazy-load images as they enter the viewport, scroll through the page first and wait for images to finish before measuring height. Very tall pages can exceed browser or image-library limits; split them into sections or use a capture service designed for full-page rendering.

Timing, fonts, and deterministic output

A screenshot is only as stable as the page state at capture time. Use explicit waits rather than a large arbitrary sleep:

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

wait = WebDriverWait(driver, 30)
driver.get("https://example.com/dashboard")
wait.until(lambda d: d.execute_script("return document.fonts.status") == "loaded")
wait.until(lambda d: d.find_element(By.CSS_SELECTOR, "[data-ready='true']").is_displayed())

For repeatable images, set a fixed viewport, device scale factor where supported, timezone, locale, and user-agent. Disable animations with injected CSS when motion causes inconsistent frames:

driver.execute_script("""
const style = document.createElement('style');
style.textContent = `*, *::before, *::after {
  animation: none !important;
  transition: none !important;
  caret-color: transparent !important;
}`;
document.head.appendChild(style);
""")

Using other languages and command-line tools

The conversion principle is language-independent: Selenium emits PNG, and a second library performs encoding. In Node.js, capture the PNG buffer with Selenium WebDriver and convert it with a package such as sharp:

Pre-capture cleanup removes overlays that would otherwise appear in the image.
Pre-capture cleanup removes overlays that would otherwise appear in the image.
import { Builder } from 'selenium-webdriver';
import sharp from 'sharp';

const driver = await new Builder().forBrowser('chrome').build();
try {
  await driver.get('https://example.com');
  const pngBase64 = await driver.takeScreenshot();
  const png = Buffer.from(pngBase64, 'base64');
  await sharp(png).flatten({ background: '#ffffff' }).jpeg({ quality: 90 }).toFile('screenshot.jpg');
} finally {
  await driver.quit();
}

From a shell, Selenium itself is not a cURL service. If you already have a PNG, ImageMagick can convert it:

magick screenshot.png -background white -alpha remove -alpha off -quality 90 screenshot.jpg

For a Python HTTP workflow that receives an image, Pillow can read the response bytes in memory:

import requests
from io import BytesIO
from PIL import Image

response = requests.get("https://example.com/screenshot.png", timeout=30)
response.raise_for_status()
with Image.open(BytesIO(response.content)) as image:
    image.convert("RGB").save("screenshot.jpg", "JPEG", quality=90)

Or skip the browser setup

If you need an image in PNG, JPEG, WebP, or PDF without maintaining WebDriver, ScreenshotNeo provides a website screenshot API. One GET request renders the URL and returns the requested artifact. The API accepts the parameter names used by many other screenshot services, which can simplify migration. See the ScreenshotNeo documentation for the complete option list.

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}`);

ScreenshotNeo can capture full pages with lazy images loaded, a single element by CSS selector, dark mode, device presets or custom viewports, retina scale, custom CSS and JavaScript, clicks, selector or network-idle waits, blocked ads and resource types, custom headers and cookies, timezone and geolocation, transparent backgrounds, image resizing, cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, and PDF output.

It removes cookie and consent banners, newsletter popups, and chat widgets before capture; each cleanup step can be disabled. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed. The response identifies the result with X-Page-Verdict and X-Billed headers. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing gives two months free, and every feature is available on every plan. Create a free ScreenshotNeo account and start with the 1,000 included screenshots.

Troubleshooting common failures

Symptom Likely cause Fix
File named .jpg is rejected as PNG PNG bytes were written with a JPEG extension Decode and re-encode with Pillow, Sharp, or ImageMagick
cannot write mode RGBA as JPEG JPEG cannot store alpha Composite onto a background, then convert to RGB
Screenshot is blank Capture occurred before navigation or rendering completed Wait for a meaningful selector, fonts, and application-ready state
Element screenshot fails Element is hidden, zero-sized, detached, or covered Wait for visibility, scroll it into view, and verify its bounding rectangle
Fonts or icons differ between runs Web fonts or external assets were not ready Wait for document.fonts.status, use stable network conditions, and set a fixed viewport
Full-page image cuts off content Window dimensions were measured before lazy content loaded Scroll through the page, wait for images, then measure and capture
WebP conversion raises an unsupported-format error Pillow was built without WebP support Install a current Pillow wheel or use another encoder; verify with features.check("webp")
Chrome fails to start in CI Browser binary, driver, sandbox, or shared-memory configuration Install compatible browser and driver versions, use headless mode, and inspect the driver log

Performance, reliability, and cost considerations

Performance

Capture PNG once and convert in memory when you need multiple output formats. This avoids opening and decoding the same file repeatedly. JPEG encoding quality, WebP method, image dimensions, and full-page height all affect CPU time and output size. Resize only after deciding whether the output must remain pixel-perfect; resizing can change text sharpness.

Reliability

Always call driver.quit() in a finally block so failed conversions do not leak browser processes. Check that the navigation completed, verify the screenshot byte length is nonzero, and inspect the output with Pillow before publishing it. For batch jobs, record the source URL, viewport, browser version, image format, and encoder settings alongside the file.

Cost

Self-hosted Selenium consumes browser, CPU, memory, storage, and maintenance resources. Conversion adds local CPU work but no Selenium-specific licensing fee. A hosted API trades browser operations for per-capture billing. With ScreenshotNeo, only clean shots are billed; failed loads, bot checks, blank pages, timeouts, and cache hits cost nothing, and the response headers show the verdict and billing result.

Checklist before shipping screenshots

  • Confirm the bytes are actually PNG, JPEG, or WebP by opening them with an image decoder.
  • Use PNG for transparency and lossless screenshots.
  • Flatten RGBA or palette images before JPEG encoding.
  • Choose and document JPEG quality or WebP settings.
  • Set a deterministic viewport, scale, locale, and timezone.
  • Wait for fonts, images, and application data before capture.
  • Use element screenshots when a full page would include irrelevant content.
  • Close WebDriver instances even when conversion fails.
  • For unattended rendering, consider an API with explicit waits, cleanup, verdict headers, and usage controls.

FAQ

Can Selenium take a JPEG screenshot directly?

No. Standard Selenium screenshot methods return or write PNG data. Convert the PNG after capture.

Does changing the extension convert the image?

No. The extension does not alter the encoded bytes. Use an image encoder.

Which format is best for screenshots?

PNG is best for lossless detail and transparency. JPEG is useful for smaller photographic images when loss is acceptable. WebP supports lossy, lossless, and transparent output when your consumers support it.

Can an element screenshot be saved as JPEG?

Yes. Capture the element’s PNG bytes, then apply the same Pillow, Sharp, or ImageMagick conversion used for a full-window screenshot.

Why does my JPEG have a black background?

The source image has transparency and was converted without choosing a background. Composite the RGBA image onto white or another intended color before converting to RGB.

When should I use a screenshot API instead of Selenium?

Use an API when you need repeatable remote captures, cleanup of consent and chat overlays, asynchronous or bulk jobs, signed links, or an MCP workflow for AI agents without managing browser drivers.