Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
The most dependable way to save a webpage as an image in Python is to render it in a real browser with Playwright, wait for the state you need, and call page.screenshot(). Use full_page=True for the entire scrollable document, a locator screenshot for one component, or omit path when you need the image bytes in memory.
This guide builds a runnable script, explains viewport and full-page captures, covers PNG, JPEG and WebP output, and shows how to make dynamic pages more repeatable.
What you need before writing the script
- Python 3.8 or newer is a practical baseline for current Playwright releases.
- A virtual environment keeps the browser automation package separate from other projects.
- Playwright’s browser binaries must be installed once after installing the Python package.
- A reachable webpage and a location where your process can write the resulting image.
Create and activate an environment, then install Playwright:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →python -m venv .venv
# macOS/Linux
source .venv/bin/activate
# Windows PowerShell
.venvScriptsActivate.ps1
python -m pip install playwright
python -m playwright install
The final command downloads the browser binaries used by Playwright. In a minimal Linux container you may need the operating system dependencies as well; run python -m playwright install --with-deps when your environment permits it.
#1 Best Overall
- CRISP CLARITY: This 23.8″ Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
- INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
- THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
- WORK SEAMLESSLY: This sleek monitor is virtually bezel-free on three sides, so the screen looks even bigger for the viewer. This minimalistic design also allows for seamless multi-monitor setups that enhance your workflow and boost productivity
- A BETTER READING EXPERIENCE: For busy office workers, EasyRead mode provides a more paper-like experience for when viewing lengthy documents
A complete Python webpage screenshot
This synchronous example opens a URL, waits for the page load event, captures the full document, and closes the browser even if an error occurs:
from pathlib import Path
from playwright.sync_api import sync_playwright
URL = "https://example.com"
OUTPUT = Path("example-full.png")
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page(viewport={"width": 1440, "height": 900}, device_scale_factor=1)
try:
page.goto(URL, wait_until="load", timeout=30_000)
page.screenshot(path=str(OUTPUT), full_page=True)
finally:
browser.close()
print(f"Saved {OUTPUT.resolve()}")
page.goto() navigates the browser. wait_until="load" waits for the document’s load event, but it does not guarantee that client-rendered data, fonts, animations or lazy images are finished. Choose a more specific readiness condition when the site needs one.
Visible viewport versus the complete document
Without full_page=True, Playwright captures the currently visible viewport (here, 1,440 × 900 CSS pixels). With it, Playwright expands the capture to the page’s full scrollable height. A very long page can produce a very tall image, so consider an element capture or a PDF for documents that are thousands of pixels high.
Save to a file or keep bytes in memory
The path argument writes the image to disk. If you omit it, page.screenshot() returns bytes, which you can upload, hash, resize or pass to an image library without creating an intermediate file:
from io import BytesIO
from PIL import Image
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com", wait_until="domcontentloaded")
image_bytes = page.screenshot(type="webp", quality=82)
image = Image.open(BytesIO(image_bytes))
print(image.size)
browser.close()
Install Pillow separately if you use that example: python -m pip install pillow.
Capture one element instead of the whole page
Use a locator when you need a header, chart, card or other component. The locator must resolve to the element you intend to capture:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(viewport={"width": 1280, "height": 800})
page.goto("https://example.com", wait_until="domcontentloaded")
page.locator("header").screenshot(path="header.png")
browser.close()
A locator screenshot follows the element’s rendered bounding box. For a selector that appears later, wait for it first:
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsRank #2
- CRISP CLARITY: This 22 inch class (21.5″ viewable) Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
- 100HZ FAST REFRESH RATE: 100Hz brings your favorite movies and video games to life. Stream, binge, and play effortlessly
- SMOOTH ACTION WITH ADAPTIVE-SYNC: Adaptive-Sync technology ensures fluid action sequences and rapid response time. Every frame will be rendered smoothly with crystal clarity and without stutter
- INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
- THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
page.locator(".report-card").wait_for(state="visible", timeout=30_000)
page.locator(".report-card").screenshot(path="report-card.png")
Choose format, quality and pixel scale
PNG, JPEG or WebP
- PNG: the default inferred when the filename ends in
.png; it preserves sharp text and supports transparency. - JPEG: useful for photographic pages and smaller files; it does not support transparency.
- WebP: a compact modern format. Set
qualitywhen using lossy WebP.
You can make the format explicit, which is helpful when returning bytes:
page.screenshot(path="page.jpg", type="jpeg", quality=85)
page.screenshot(path="page.webp", type="webp", quality=82)
page.screenshot(path="page.png", type="png")
The quality option applies to JPEG and WebP, not PNG. Lower values generally reduce file size at the cost of visible compression.
CSS pixels versus device pixels
The scale screenshot option controls output dimensions. scale="css" keeps one output pixel per CSS pixel and produces smaller high-DPI captures; scale="device" uses device pixels and produces a denser image. You can also set the browser context’s device_scale_factor:
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(viewport={"width": 1200, "height": 800}, device_scale_factor=2)
page.goto("https://example.com")
page.screenshot(path="retina.png", scale="device")
browser.close()
Use a known viewport and scale when comparing screenshots in tests. A different viewport can change responsive layout, line wrapping and lazy-loading behavior.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Transparent backgrounds
Set omit_background=True to preserve transparency where the page supports it. This option does not apply to JPEG, so use PNG or WebP for transparent output:
page.screenshot(path="transparent.png", omit_background=True)
Wait for the page state that matters
There is no universal “ready” signal for every website. Pick a condition tied to the content you need:
Wait for a specific element
page.goto("https://example.com/dashboard", wait_until="domcontentloaded")
page.locator("[data-testid='dashboard-loaded']").wait_for(state="visible")
page.screenshot(path="dashboard.png", full_page=True)
Wait for a short, known delay
page.goto("https://example.com", wait_until="load")
page.wait_for_timeout(1_500)
page.screenshot(path="after-delay.png")
A fixed delay is simple but can be too short on a slow run and wasteful on a fast one. Prefer a selector or application-specific signal when possible.
Rank #3
- Clear visuals. Fluid motion: A 144Hz refresh rate and 1ms MPRT deliver smooth, tear‑free motion across work, gaming, and streaming for clearer, more fluid viewing.
- Eye comfort: TÜV Rheinland 3‑star* certification reduces harmful blue light while preserving stunning color quality without compromise. *TÜV Rheinland 3-star eye comfort certification.
- Wide viewing angle: Get consistent views across a wide 178° /178° viewing angle.
- In-Plane Switching (IPS): See excellent color accuracy and consistency across wide viewing angles with In-plane Switching (IPS) technology.
- Ultra-thin bezels: Maximize your viewing experience with thin bezels.
Reduce animation differences
Animations can make two captures differ. Playwright’s screenshot API provides animation control; disabling animations for the capture is useful for visual comparisons:
page.screenshot(path="stable.png", animations="disabled")
If a site changes content after navigation, wait for that site’s network or application signal rather than assuming the load event means all data is complete.
Use the screenshot timeout deliberately
The documented screenshot timeout default is 30 seconds. Set a value appropriate for your job and catch timeout errors so a batch process can report the URL that failed:
page.set_default_timeout(30_000)
page.set_default_navigation_timeout(45_000)
try:
page.goto("https://example.com", wait_until="networkidle")
page.screenshot(path="capture.png", timeout=30_000)
except Exception as exc:
print(f"Capture failed: {exc}")
networkidle can be a poor choice for pages with analytics, ads or persistent connections. A visible application element is often more reliable.
Reusable script with command-line arguments
This version accepts a URL, output path and optional full-page flag, making it suitable for a small utility or CI job:
import argparse
from pathlib import Path
from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeoutError
def capture(url: str, output: Path, full_page: bool) -> None:
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page(viewport={"width": 1440, "height": 900})
try:
page.goto(url, wait_until="domcontentloaded", timeout=45_000)
page.screenshot(
path=str(output),
full_page=full_page,
animations="disabled",
timeout=30_000,
)
finally:
browser.close()
parser = argparse.ArgumentParser()
parser.add_argument("url")
parser.add_argument("-o", "--output", default="screenshot.png")
parser.add_argument("--full-page", action="store_true")
args = parser.parse_args()
try:
capture(args.url, Path(args.output), args.full_page)
print(f"Saved {args.output}")
except PlaywrightTimeoutError as exc:
raise SystemExit(f"Timed out while loading or capturing {args.url}: {exc}")
Run it with python capture.py https://example.com --full-page -o example.png. Validate URLs and output directories before using this utility with untrusted input.
Common failures and precise fixes
“Executable doesn’t exist” or browser launch failure
Install the browser binaries after installing the package: python -m playwright install. On supported Linux images, add --with-deps if system libraries are missing.
Rank #4
- CURVED FOR ENHANCED ENGAGEMENT: An immersive viewing experience with a curved monitor that wraps more closely around your field of vision; It creates a wider view, enhancing depth perception and minimizing peripheral distraction
- SMOOTH PERFORMANCE FOR SEAMLESS CONTENT: Stay in the action when playing games, watching videos, or working on creative projects; The 100Hz refresh rate reduces lag and motion blur so you don't miss a thing in fast-paced moments¹
- MORE GAMING POWER: Gain the edge with optimizable game settings; Color and image contrast can be adjusted to see scenes more vividly and spot enemies hiding in the dark; Game Mode adjusts any game to fill the screen so you can view every detail²
- KEEP IT EASY ON THE EYES: Care for your eyes and stay comfortable, even during long sessions; Advanced eye comfort technology certified by TÜV reduces eye strain by minimizing blue light and reducing irritating screen flicker²
- INCREASED VERSATILITY: Connect to more; Plug devices straight into your monitor for increased flexibility, making your computing environment even more convenient
The screenshot is blank or incomplete
Check that navigation actually reached the expected URL, then wait for a content selector instead of capturing immediately. For lazy content, scroll or use the page’s own load signal before taking a full-page shot. A blank result can also indicate a site-side bot check or an application error rather than a Python problem.
Timeout during navigation
Increase the navigation timeout for a slow site, use wait_until="domcontentloaded" instead of waiting for every network request, and identify a selector that proves the required content is ready. Log the final URL and exception text.
Free tools Windows power users keep installed
One-click scans. No signup required.
Element locator matches nothing
Inspect the selector, wait for the element, and account for iframes. Content inside an iframe must be addressed through the appropriate frame rather than the top-level page. A locator that matches multiple elements may need a more specific selector or an explicit .first.
Fonts, layout or colors differ between runs
Use the same browser engine, viewport, device scale factor and color scheme. Wait for the relevant fonts and data, disable animations, and avoid capturing while a responsive breakpoint is changing. Containerized runs may render fonts differently if the required font is not installed or loaded by the page.
The output file is unexpectedly huge
Use JPEG or WebP with an appropriate quality value, capture only the needed element, reduce the viewport or use scale="css". Full-page PNGs preserve detail but can become very large on long documents.
Permission denied when writing
Write to a directory owned by the process and create it first with Path(output).parent.mkdir(parents=True, exist_ok=True). Avoid relying on the current working directory in services; use an explicit absolute path.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Operational notes for reliable batches
- Launch one browser and create a fresh page or context per job when processing several URLs; this avoids paying browser startup cost for every capture while keeping page state isolated.
- Set explicit navigation and screenshot timeouts, record the URL and final address, and retain the exception text for retries.
- Use a bounded concurrency level. Too many simultaneous pages can exhaust CPU, memory or network bandwidth and make captures less reliable.
- Save to a temporary filename and rename it after a successful screenshot so downstream readers never consume a partial file.
- Respect access controls, robots policies, authentication requirements and the website’s terms. Do not place credentials in URLs or source code; use Playwright context headers or secrets management where appropriate.
- For visual regression, pin browser versions in your environment and compare captures at identical viewport, scale and color settings.
Or skip the browser setup
ScreenshotNeo is a hosted screenshot API and MCP server. It accepts a URL in one GET request and returns PNG, JPEG, WebP or PDF. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be switched off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the page verdict and billing result in X-Page-Verdict and X-Billed headers.
Use the documented API examples at https://screenshotneo.com/docs/:
Best Value
- 【INTEGRATED SPEAKERS】Whether you're at work or in the midst of an intense gaming session, our built-in speakers provide rich and seamless audio, all while keeping your desk clutter-free.
- 【EASY ON THE EYES】 Protect your eyes and enhance your comfort with Blue-Light Shift technology. This feature reduces harmful blue light emissions from your screen, helping to alleviate eye strain during long hours of use and promoting healthier viewing habits.
- 【WIDEN YOUR PERSPECTIVE】Our sleek minimal bezel design ensures undivided attention. The nearly bezel-free display seamlessly connects in a dual monitor arrangement, delivering an unobstructed view that lets you focus on more at once, completely distraction-free.
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}`);
It also supports full-page and element captures, dark mode, device presets and custom viewports, retina scale, PDF paper and page options, custom CSS and JavaScript, click and wait rules, request blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API and an OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify migration. Its MCP server provides take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients.
Pricing is Free for 1,000 shots per month with no card, then Starter $5 for 3,000, Growth $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. Sign up free to get 1,000 screenshots a month without a card.
PC 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 & 11Crashes, 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 minuteFrequently asked questions
Can Python save a screenshot without opening a visible browser window?
Yes. Playwright launches Chromium headlessly when you pass headless=True (the default in the examples), while still rendering the page through a browser engine.
Why is my full-page image extremely tall?
Full-page mode includes the document’s entire scrollable height. Capture a specific locator, split the page into sections, or produce a PDF when a single tall bitmap is impractical.
Should I use Selenium instead?
The procedure here uses Playwright because its current Python documentation directly covers full-page, locator, in-memory and format-specific screenshot options. Selenium behavior and code should be checked against the version and driver you deploy rather than copied from older examples.
How can I send the screenshot to an HTTP response?
Omit path, keep the returned bytes, and send them with the appropriate image media type from your web framework. This avoids a temporary file and lets the caller choose how to store the result.
Frequently Asked Questions
Can Python save a screenshot without opening a visible browser window?
Yes. Playwright launches Chromium headlessly when you pass headless=True (the default in the examples), while still rendering the page through a browser engine.
Why is my full-page image extremely tall?
Full-page mode includes the document’s entire scrollable height. Capture a specific locator, split the page into sections, or produce a PDF when a single tall bitmap is impractical.
Should I use Selenium instead?
The procedure here uses Playwright because its current Python documentation directly covers full-page, locator, in-memory and format-specific screenshot options. Selenium behavior and code should be checked against the version and driver you deploy rather than copied from older examples.
How can I send the screenshot to an HTTP response?
Omit path, keep the returned bytes, and send them with the appropriate image media type from your web framework. This avoids a temporary file and lets the caller choose how to store the result.
Recommended Free Tools
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.

