Free tools Windows power users keep installed
One-click scans. No signup required.
Use pathlib.Path.mkdir(parents=True, exist_ok=True) to create the destination folder, join the filename with that folder, and pass the resulting path to Selenium’s driver.save_screenshot(). Check the method’s Boolean result so an I/O failure cannot pass silently.
Complete Python example
This script creates a screenshots directory if necessary, opens a page, saves the current browser window as a PNG, and raises an error if Selenium reports that the file could not be written.
from pathlib import Path
from selenium import webdriver
screenshot_dir = Path("screenshots")
screenshot_dir.mkdir(parents=True, exist_ok=True)
# Configure the driver that your project uses.
driver = webdriver.Chrome()
try:
driver.get("https://example.com")
screenshot_path = screenshot_dir / "page.png"
saved = driver.save_screenshot(str(screenshot_path))
if not saved:
raise OSError(f"Could not save screenshot to {screenshot_path}")
finally:
driver.quit()
Run the program from your project directory. The relative path screenshots/page.png is resolved against the Python process’s current working directory, not necessarily the directory containing the script.
Why this pattern works
Create the directory before saving
Path.mkdir(parents=True, exist_ok=True) handles both a first run and later runs. parents=True creates missing ancestors, so a path such as artifacts/ui/screenshots works even when none of those directories exists. exist_ok=True prevents an exception when the target directory is already present.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
#1 Best Overall
- Easily store and access 2TB to content on the go with the Seagate Portable Drive, a USB external hard drive
- Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop
- To get set up, connect the portable hard drive to a computer for automatic recognition no software required
- This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
- The available storage capacity may vary.
Join paths instead of concatenating strings
The expression screenshot_dir / "page.png" uses pathlib‘s path-aware joining. It avoids manually choosing slash or backslash separators and makes it easy to replace the output directory or filename later.
Use a PNG filename
Selenium’s save_screenshot(filename) API writes the current browser window as a PNG and expects a full filename ending in .png. Converting the Path with str() is explicit and compatible with WebDriver implementations that document a string filename.
Check the return value
The method returns a Boolean. A false result indicates an I/O error. Treat it as a failed test artifact and report the path, rather than continuing as though the screenshot exists.
Choose an output location deliberately
Relative project folder
from pathlib import Path
output_dir = Path("test-output") / "screenshots"
output_dir.mkdir(parents=True, exist_ok=True)
path = output_dir / "login.png"
This is convenient for local development and CI jobs that collect a known workspace folder. Remember that the process working directory can differ between an IDE, a shell, and a CI runner.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Folder beside the Python file
from pathlib import Path
project_root = Path(__file__).resolve().parent
output_dir = project_root / "screenshots"
output_dir.mkdir(parents=True, exist_ok=True)
path = output_dir / "page.png"
Using __file__ makes the location independent of where the command was launched. In packaged applications, use the application’s configured writable directory instead; an installation directory may be read-only.
Rank #2
- Easily store and access 5TB of content on the go with the Seagate portable drive, a USB external hard Drive
- Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop
- To get set up, connect the portable hard drive to a computer for automatic recognition software required
- This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
- The available storage capacity may vary.
Absolute path supplied by configuration
import os
from pathlib import Path
output_dir = Path(os.environ.get("SCREENSHOT_DIR", "screenshots")).expanduser()
output_dir.mkdir(parents=True, exist_ok=True)
path = output_dir / "page.png"
Configuration is useful when a CI system mounts an artifact volume or when different operating systems use different output roots.
Preserve every capture instead of overwriting
Selenium writes to the filename you provide. Reusing page.png replaces the previous capture. Add a timestamp, test name, or unique identifier when each result matters.
from datetime import datetime, timezone
from pathlib import Path
output_dir = Path("screenshots")
output_dir.mkdir(parents=True, exist_ok=True)
stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
path = output_dir / f"checkout-{stamp}.png"
if not driver.save_screenshot(str(path)):
raise OSError(f"Could not save screenshot to {path}")
For parallel tests, include a worker or test identifier as well as the timestamp. A timestamp alone can still collide when multiple processes start in the same second.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11What Selenium actually captures
Current browser window
driver.save_screenshot() captures the current window at the moment the call runs. It does not automatically capture every pixel below the viewport. Wait until navigation and relevant asynchronous content have completed before calling it.
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
wait = WebDriverWait(driver, 20)
driver.get("https://example.com/dashboard")
wait.until(lambda d: d.find_element(By.CSS_SELECTOR, "main.loaded"))
path = Path("screenshots") / "dashboard.png"
path.parent.mkdir(parents=True, exist_ok=True)
if not driver.save_screenshot(str(path)):
raise OSError("Screenshot save failed")
A single element
When the requirement is a component rather than the viewport, call screenshot() on the element.
Rank #3
- Easily store and access 1TB to content on the go with the Seagate Portable Drive, a USB external hard drive.Specific uses: Personal
- Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop. Reformatting may be required for Mac
- To get set up, connect the portable hard drive to a computer for automatic recognition no software required
- This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
- The available storage capacity may vary.
element = driver.find_element(By.CSS_SELECTOR, "#invoice-summary")
element_path = Path("screenshots") / "invoice-summary.png"
element_path.parent.mkdir(parents=True, exist_ok=True)
if not element.screenshot(str(element_path)):
raise OSError(f"Could not save element screenshot to {element_path}")
This API also writes PNG output and returns a Boolean. The element must be present and visible enough for the browser driver to capture it.
Full-document images
Full-page behavior is separate from the ordinary current-window call. Firefox’s Python WebDriver API exposes full-document screenshot methods; support and behavior differ by browser and driver. If portability across Chrome, Edge, and Firefox is required, verify the specific driver APIs rather than assuming that save_screenshot() means full document.
Reusable helper function
Centralize directory creation, naming, and error handling so every test follows the same policy.
from pathlib import Path
from typing import Union
PathLike = Union[str, Path]
def save_window_screenshot(driver, directory: PathLike, filename: str) -> Path:
output_dir = Path(directory)
output_dir.mkdir(parents=True, exist_ok=True)
if not filename.lower().endswith(".png"):
raise ValueError("Selenium screenshots must use a .png filename")
destination = output_dir / filename
if not driver.save_screenshot(str(destination)):
raise OSError(f"Could not save screenshot to {destination}")
return destination
# Example:
path = save_window_screenshot(driver, "artifacts/screenshots", "home.png")
print(f"Saved {path.resolve()}")
Keep filenames controlled by your application. If a test name can contain slashes, colons, or other platform-specific characters, sanitize it before using it as a filename.
Troubleshooting
“No such file or directory”
The parent directory was not created, or an ancestor is missing. Build the destination with Path and call destination.parent.mkdir(parents=True, exist_ok=True) before saving.
Rank #4
- Easily store and access 4TB of content on the go with the Seagate Portable Drive, a USB external hard drive.Specific uses: Personal
- Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop
- To get set up, connect the portable hard drive to a computer for automatic recognition no software required
- This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
- The available storage capacity may vary.
The method returns False
This is Selenium’s documented I/O-failure result. Print Path.resolve() to confirm the actual target, check that the parent exists, verify write permission, and ensure another process has not locked or replaced the path. In a container or CI job, confirm that the destination volume is mounted and writable.
FileExistsError from mkdir
Use exist_ok=True when the path is expected to be an existing directory. If the target path exists but is a file, fix the configuration or choose another directory; exist_ok=True cannot turn a file into a directory.
The file is in an unexpected folder
Relative paths follow the process working directory. Log Path.cwd(), use an absolute configured root, or derive the path from Path(__file__).resolve().parent.
The image shows the wrong state
Capture only after navigation, animations, lazy content, and required elements are ready. Use an explicit wait for a meaningful selector instead of a fixed sleep whenever possible. For a component, use WebElement.screenshot(); for a document-length image, use a browser-specific full-document method that your target browser supports.
Old files disappear
The same filename is being reused. Add a test identifier, UTC timestamp, or unique ID, and have your CI artifact-retention policy clean up old files separately.
Best Value
- [Upgraded Version] - This external hard drive features a mirrored logo stripe combined with a striped anti-slip design, and the rounded corners of the casing make it easier to grip. The stripes also have a heat dissipation function, ensuring stable and fast data transfer.
- 【Ultra-thin and quiet】 - The motherboard adopts JMicron 578 noise-free solution, giving you a quiet working environment. Lightweight and portable size designed to fit in your pocket for easy portability.
- 【Ultra-Fast Data Transfers】 - Pairing this external hard drive with JMicron 578 solution USB 3.0 and USB 2.0 interfaces enables blazing-fast data transfer. It boasts theoretical read speeds of up to 125MB/s and write speeds of up to 103MB/s.
- 【Plug and Play】 - With no software to install, just plug it in and the drive is ready to use.The hard disk chip is wrapped with an aluminum anti-interference layer to increase heat dissipation and protect data.
- 【What You Get】 - 1 x Portable Hard Drive, 1 x USB 3.0 Cable, 1 x User Manual, Gift-type shell packaging ,Three-year manufacturer's warranty and free technical support services.
Operational considerations
- Permissions: choose a writable workspace, especially in containers, services, and hosted CI runners.
- Cleanup: decide whether screenshots are debugging artifacts, permanent test evidence, or temporary files. Apply retention rules outside the save function.
- Failure handling: preserve the original test exception while recording screenshot-save failures, so an artifact problem does not hide the actual browser failure.
- Concurrency: give each worker its own subdirectory or collision-resistant filename.
- Verification: check the Boolean result and, where your workflow needs stronger guarantees, confirm that the resulting file exists and has a nonzero size.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF, so you do not need to install or manage a Selenium browser for a straightforward URL capture. Before the capture, it accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and whether the request was billed. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
See the complete parameter reference in the ScreenshotNeo documentation. This cURL request saves a WebP response:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The equivalent Python request is:
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)
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}`);
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()));
ScreenshotNeo includes full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets plus custom viewports, retina scale, PDF paper settings and page ranges, custom CSS and JavaScript, pre-capture clicks, selector hiding, waits for selectors, delays or network idle, request and resource blocking, headers, cookies, user agents, Authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, an OpenAPI specification, and compatibility with parameter names used by other screenshot APIs.
Plans include 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000 shots. Growth is $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000; yearly billing gives two months free, and every feature is included on every plan. Create a free ScreenshotNeo account to start with the 1,000 monthly screenshots.
Frequently Asked Questions
Can I pass a pathlib.Path directly to Selenium?
Python paths implement the filesystem path protocol, but converting the path with str() matches Selenium’s documented filename parameter and is explicit across WebDriver implementations.
Does save_screenshot capture the whole page?
The ordinary driver method captures the current window. Use an element screenshot for one component or a browser-specific full-document API when you need content beyond the viewport.
Why should I use exist_ok=True?
It makes repeated test runs safe when the destination directory already exists. It does not fix a path whose target is an existing file.
Quick Recap
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.
Recommended Free Tools

