How to Capture Multiple Website Screenshots with Selenium in Python for Bulk Testing
Capture a list of websites with one Selenium session, consistent viewport settings, readiness waits, safe filenames, and per-page failure reporting.
To capture multiple website screenshots for bulk testing, use one Selenium WebDriver session, navigate to each URL, wait for the page state your test needs, and save the current browser window with driver.save_screenshot(). Set a consistent viewport, give every output a unique filename, check whether each save succeeded, and always close the driver.
The example below uses Python and Chrome. Selenium’s screenshot method captures the current window as a PNG; it does not by itself promise a full-page capture. See the Selenium WebDriver API for the screenshot and window-size methods.
1. Prepare the URL list and output directory
Keep the URLs in the test’s source of truth, such as a fixture or input file. Create the output directory before capturing. Use a filename that includes an index as well as a hostname: multiple URLs can share a host, and a hostname alone could overwrite an earlier image.
from pathlib import Path
from urllib.parse import urlparse
urls = [
"https://example.com/",
"https://www.selenium.dev/",
]
output_dir = Path("screenshots")
output_dir.mkdir(parents=True, exist_ok=True)
def screenshot_name(index: int, url: str) -> str:
host = urlparse(url).netloc.replace(":", "_") or "page"
return f"{index:03d}_{host}.png"
The index makes names unique for a single run even when URLs share a hostname. If your list can contain the same URL more than once, retain the index rather than deduplicating unless the test explicitly treats those entries as equivalent.
2. Capture each page in one Selenium session
Install Selenium and make a compatible browser and driver available in the environment according to your project’s setup. The code below uses Chrome’s headless mode, chooses a repeatable viewport, and saves each current window as a PNG.
from pathlib import Path
from urllib.parse import urlparse
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
urls = [
"https://example.com/",
"https://www.selenium.dev/",
]
output_dir = Path("screenshots")
output_dir.mkdir(parents=True, exist_ok=True)
options = webdriver.ChromeOptions()
options.add_argument("--headless")
driver = webdriver.Chrome(options=options)
driver.set_window_size(1440, 1000)
try:
for index, url in enumerate(urls, start=1):
try:
driver.get(url)
# Replace this with a condition that represents readiness
# for the page or component your test actually checks.
WebDriverWait(driver, 20).until(
lambda browser: browser.execute_script(
"return document.readyState"
) == "complete"
)
host = urlparse(url).netloc.replace(":", "_") or "page"
path = output_dir / f"{index:03d}_{host}.png"
saved = driver.save_screenshot(str(path))
if not saved:
print(f"Screenshot save failed: {url} -> {path}")
except Exception as exc:
print(f"Capture failed: {url}: {type(exc).__name__}: {exc}")
finally:
driver.quit()
The readiness condition is part of the test, not a universal definition of a loaded page. Replace it with a wait for the element or state that matters—for example, a results panel becoming visible or a loading indicator disappearing. A page can finish its initial document load while application data, images, or animations are still changing.
save_screenshot() returns a Boolean; log a false result as a save failure. The API documents a PNG filename and recommends an absolute path. The example uses a path rooted in the process working directory; use output_dir.resolve() if your runner requires an absolute destination. The driver is shut down in finally, including when navigation or capture raises an exception.
3. Make bulk captures repeatable
- Fix the viewport. Use the same width and height on every run. A viewport change can alter responsive breakpoints and page layout.
- Fix browser and test state. Keep the browser version, test data, authentication state, locale, and other relevant setup consistent when comparing images.
- Wait for the state under test. Prefer an explicit condition over a short arbitrary sleep. For dynamic content, wait for the particular content or state whose appearance matters.
- Choose capture scope accurately. The documented Selenium method captures the current window. Do not treat it as a full-page screenshot method. If the test needs content below the viewport, select a separately verified full-page approach or test a viewport-sized region.
- Record failures per URL. Include the URL and destination in logs or the test report so one failed page does not hide the status of the rest of the batch.
- Keep outputs isolated when runs overlap. Use a per-run directory or unique run identifier if concurrent jobs write into the same location.
For a simple headless command-line capture of one URL, Chrome also documents the --screenshot and --window-size options. The Chrome Headless CLI reference shows that route; Selenium is useful when the bulk run needs a programmed URL list, browser interaction, or per-page test state.
4. Troubleshoot common failures
| Symptom | Likely cause | What to do |
|---|---|---|
| Chrome or WebDriver fails to start | The browser or driver is missing, incompatible, or unavailable in the runner environment. | Check the environment’s browser and driver setup and use versions supported together. The research sources describe Selenium behavior, not a specific installation procedure. |
| Screenshot file is missing or empty | The destination directory does not exist, the process cannot write there, or the save operation failed. | Create the directory, check filesystem permissions, use a full destination path, and inspect the Boolean returned by save_screenshot(). |
| Several pages overwrite one image | Filenames are based only on a repeated hostname or constant name. | Add an index or another unique identifier to every filename. |
| Image shows a loading state or incomplete content | The page’s meaningful content was not ready when the screenshot was taken. | Wait for an application-specific element or state. Increase the timeout only if the expected state can legitimately take longer; investigate pages that never reach it. |
| Screenshot dimensions or layout differ between runs | The viewport, browser, page data, or user state changed. | Control these inputs and make sure the same readiness condition is used for every run. |
| Only the visible viewport appears | The method captures the current window, not a guaranteed full-page image. | Use a verified full-page capture technique if the test requires it, and document that behavior separately from save_screenshot(). |
| Later URLs fail after an earlier exception | An uncaught error ended the loop. | Catch and report exceptions per URL when the batch should continue, while retaining the outer finally so driver.quit() always runs. |
5. Performance, reliability, and cost
A single browser session avoids repeatedly starting a browser for each URL, while still allowing the script to navigate and interact between captures. Each page’s navigation and readiness wait will usually determine how long the batch takes. Keep waits tied to the actual test condition, and choose a timeout that allows expected variability without making genuine failures slow to report.
For reliability, isolate exceptions per URL if the rest of the batch should continue, write a clear result for every attempted capture, and close the browser in a finally block. A successful file save only tells you the screenshot was written; it does not establish that the page rendered the expected content. Add the application-specific assertions your visual test needs.
The Selenium workflow runs a browser in your own environment, so account for that environment’s browser execution and maintenance when planning the batch. No benchmark or per-capture infrastructure cost is asserted here; actual time and cost depend on your runner, pages, and test setup.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. One GET request captures a URL as PNG, JPEG, WebP, or PDF. For a quick bulk-testing alternative, make one request per URL; ScreenshotNeo also has a bulk capture option for up to 100 URLs per call. See the API documentation for parameters and response 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,
)
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}`);
- Cookie banners are accepted and removed before capture, along with known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off.
- Bot checks, blank pages, timeouts, and failed loads are not billed. Cache hits are also free, and response headers identify the page verdict and billing status.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools 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 screenshots; every feature is available on every plan.
Sign up free for 1,000 screenshots a month, with no card required.
FAQ
Can I use one driver for the whole URL list?
Yes. Navigate sequentially in one session when the pages can share the same browser setup. Use a fresh session when the test requires isolated browser state.
Does Selenium save screenshots as JPEG?
The documented save_screenshot() method saves PNG. The file path should use a .png extension.
Should I wait for network idle?
Wait for the signal that represents readiness for your test. An application-specific element or state is often a clearer target than assuming that all network activity must stop.
Where can I find the Selenium shutdown pattern?
Selenium’s windows and tabs documentation includes a Python screenshot example that shuts down the driver after capture.


