How to Write Selenium Code to Take a Screenshot
Learn how to capture current windows, elements, and full pages with Selenium in Python, handle waits and errors, and save reliable PNG screenshots.
The shortest Python Selenium screenshot is driver.save_screenshot("page.png"). It captures the current browser window and writes a PNG file. Navigate first, wait for dynamic content when necessary, check the Boolean result, and always call driver.quit() in a finally block.
Selenium documents screenshots for the current browsing context, while element screenshots and browser-specific full-page methods use different APIs. Choose the capture scope before writing the code.
1. Install Selenium and a browser driver
Install the Python binding:
python -m pip install selenium
Install a supported browser such as Chrome, Firefox, or Edge. Recent Selenium versions can manage compatible drivers automatically in many setups. In restricted environments, install the driver separately and provide its path through the corresponding browser options.
2. Take a screenshot of the current window
This complete example creates its output directory, opens a page, saves a PNG, checks whether Selenium reported success, and closes the browser even if navigation or saving fails.
from pathlib import Path
from selenium import webdriver
output = Path("screenshots")
output.mkdir(parents=True, exist_ok=True)
driver = webdriver.Chrome()
try:
driver.get("https://example.com")
saved = driver.save_screenshot(str(output / "page.png"))
if not saved:
raise OSError("Selenium could not save the screenshot")
finally:
driver.quit()
save_screenshot saves the current window as a PNG and returns True when the write succeeds. The Python API expects a filename ending in .png; use an absolute path when the process working directory may vary. Selenium’s official documentation shows the same driver-level method: WebDriver screenshots.
3. Wait for pages that render after load
driver.get() waits for the page’s load event, but JavaScript applications can continue rendering afterward. Wait for a meaningful element instead of adding an arbitrary long sleep.
from pathlib import Path
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
output = Path("screenshots")
output.mkdir(exist_ok=True)
driver = webdriver.Chrome()
try:
driver.get("https://example.com/dashboard")
WebDriverWait(driver, 20).until(
EC.visibility_of_element_located((By.CSS_SELECTOR, "main"))
)
if not driver.save_screenshot(str(output / "dashboard.png")):
raise OSError("Screenshot write failed")
finally:
driver.quit()
Use an explicit wait for a selector, visible text, a URL change, or another condition that proves the page is ready. A fixed delay is useful only when no observable condition exists:
import time
time.sleep(2)
4. Capture one element
Use an element screenshot when you need a chart, card, form, or other component rather than the entire viewport.
from pathlib import Path
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
output = Path("screenshots")
output.mkdir(exist_ok=True)
driver = webdriver.Chrome()
try:
driver.get("https://example.com")
chart = WebDriverWait(driver, 20).until(
EC.visibility_of_element_located((By.CSS_SELECTOR, "#chart"))
)
chart.screenshot(str(output / "chart.png"))
finally:
driver.quit()
The element must exist and be rendered. If it is outside the viewport, Selenium or the browser may scroll it into view; for predictable results, scroll or wait for visibility before capturing.
5. Capture a full document
driver.save_screenshot() describes the current window, so it is not a portable full-page API. Selenium’s Python Firefox API documents save_full_page_screenshot():
from selenium import webdriver
driver = webdriver.Firefox()
try:
driver.get("https://example.com/long-page")
driver.save_full_page_screenshot("long-page.png")
finally:
driver.quit()
Treat this method as Firefox-specific. For Chrome or other drivers, full-document capture may require browser-specific behavior, scrolling and stitching, or a dedicated screenshot service. Test the exact browser and driver combination you deploy.
6. Keep the screenshot in memory
When another function uploads or processes the image, avoid a temporary file.
from selenium import webdriver
driver = webdriver.Chrome()
try:
driver.get("https://example.com")
png_bytes = driver.get_screenshot_as_png()
base64_text = driver.get_screenshot_as_base64()
finally:
driver.quit()
get_screenshot_as_png() returns PNG bytes. get_screenshot_as_base64() returns Base64 text suitable for embedding in HTML or sending through a text-only transport.
7. Control viewport, device scale, and browser mode
Set the viewport before navigation when layout matters:
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("desktop.png")
finally:
driver.quit()
Headless mode is convenient for servers and CI. Match the production viewport, zoom level, fonts, locale, timezone, and device pixel ratio when visual output must be comparable. Browser screenshots can differ when fonts, animations, network responses, or OS rendering differ.
8. JavaScript Selenium equivalent
Selenium’s JavaScript binding returns screenshot data as Base64. Write it as a PNG file:
import { Builder } from "selenium-webdriver";
import fs from "node:fs/promises";
const driver = await new Builder().forBrowser("chrome").build();
try {
await driver.get("https://example.com");
const base64 = await driver.takeScreenshot();
await fs.writeFile("page.png", Buffer.from(base64, "base64"));
} finally {
await driver.quit();
}
Other official Selenium bindings include Java, C#, and Ruby. Their APIs differ in whether they return a file, bytes, or Base64 data, but the workflow is the same: create a driver, navigate, wait, capture, persist the result, and quit.
9. Common errors and fixes
| Error or symptom | Cause | Fix |
|---|---|---|
SessionNotCreatedException |
Browser and driver versions or paths do not match. | Update the browser and driver, let Selenium manage the driver, or configure the correct executable path. |
WebDriverException: cannot find Chrome |
The browser is not installed or is unavailable to the process. | Install the browser or set its binary location in browser options. |
TimeoutException |
The selector never became present or visible. | Verify the selector, authentication state, frame, and page URL; increase the wait only when the page genuinely needs more time. |
| Screenshot is blank or incomplete | Capture occurred before asynchronous content, images, or fonts finished rendering. | Wait for a page-specific condition, scroll lazy content into view, or wait for images and fonts before capture. |
save_screenshot returns False |
The output could not be written. | Create the parent directory, use a writable absolute path, and keep the .png extension. |
Element screenshot raises NoSuchElementException |
The selector is wrong, the element is inside an iframe, or it has not loaded. | Switch to the correct frame and use an explicit wait. |
| Unexpected cookie banner, popup, or chat widget | The page displays visitor overlays during automation. | Dismiss the overlay in Selenium before capture, hide it with test CSS, or use a capture API that handles these elements. |
| Full-page method is missing | Full-document support is browser-specific. | Use the supported method for your browser, stitch viewport captures, or use a screenshot service. |
10. Reliability and performance checklist
- Use explicit waits tied to page state.
- Run
quit()infinallyso failed jobs do not leave browser processes running. - Use a unique output filename when parallel workers capture the same URL.
- Keep browser and driver versions pinned in CI where reproducibility matters.
- Disable animations with injected CSS when comparing screenshots.
- Capture at a fixed viewport and locale.
- Record the URL, timestamp, browser version, viewport, and wait condition with each artifact.
- Reuse a driver for a batch only when isolation requirements allow it; restart it when state leakage or memory growth becomes a problem.
Launching a browser is slower and uses more memory than an HTTP request. Parallel browsers can improve throughput but consume CPU and RAM quickly. Limit concurrency, set navigation and wait timeouts, and clean up abandoned sessions.
11. Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or PDF, with options for full-page capture, lazy images, element selectors, dark mode, device presets, custom viewports, retina scale, waits, custom CSS and JavaScript, click actions, hidden selectors, blocked resources, headers, cookies, user agents, authorization, timezone, geolocation, transparency, resizing, caching, signed links, asynchronous jobs, webhooks, bulk capture, and usage reporting. See the ScreenshotNeo API documentation for parameter details.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
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 failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots.
Create a free ScreenshotNeo account.
12. FAQ
Does Selenium save JPEG or WebP?
The documented Python screenshot method writes PNG. Convert the PNG afterward with an image library if another format is required.
Why is my screenshot only the visible viewport?
The general driver method captures the current window. Full-document capture requires a browser-specific API or a separate scrolling and stitching approach.
Can I capture a page that requires login?
Yes. Authenticate through Selenium first, or restore cookies and session state before navigating to the capture URL.
Should I use a screenshot API instead of Selenium?
Use Selenium when you need browser interaction or test assertions. Use an API when you want a simple request, managed browser infrastructure, batch jobs, or consistent cleanup of common overlays.


