How to Take a Screenshot of an Open Website in Python
Capture a page controlled by Python with Playwright or Selenium, save full-page and element images, troubleshoot failures, or use ScreenshotNeo.
Direct answer: if Python controls the browser page, call page.screenshot(path="screenshot.png") with Playwright or driver.save_screenshot("screenshot.png") with Selenium. These APIs capture the page or window in the automation session. They do not capture an unrelated browser tab that happens to be open on your desktop.
For new Python automation, Playwright gives you straightforward page, full-page, and element screenshots. Selenium is useful when your project already uses WebDriver.
1. Capture a page with Playwright Python
Install Playwright and its browser binaries:
python -m pip install playwright
python -m playwright install chromium
This complete script opens a page and saves the current viewport as a PNG:
from playwright.sync_api import sync_playwright
with sync_playwright() as playwright:
browser = playwright.chromium.launch()
page = browser.new_page()
page.goto("https://example.com", wait_until="load")
page.screenshot(path="screenshot.png")
browser.close()
If you already have a Playwright Page object pointing at the website, the essential operation is:
page.screenshot(path="screenshot.png")
Playwright infers the image type from the filename extension. PNG, JPEG, and WebP are supported by the screenshot API. Without a path, the method returns image bytes, which is useful for uploads or further processing:
image_bytes = page.screenshot()
with open("screenshot.png", "wb") as output:
output.write(image_bytes)
Full-page screenshot
Use full_page=True to capture the entire scrollable document instead of only the visible viewport:
from playwright.sync_api import sync_playwright
with sync_playwright() as playwright:
browser = playwright.chromium.launch()
page = browser.new_page(viewport={"width": 1440, "height": 900})
page.goto("https://example.com", wait_until="networkidle")
page.screenshot(path="full-page.png", full_page=True)
browser.close()
Very long pages can create large images. Set a deliberate viewport and consider JPEG or WebP when file size matters.
Screenshot one element
Locate the element and call its screenshot method:
from playwright.sync_api import sync_playwright
with sync_playwright() as playwright:
browser = playwright.chromium.launch()
page = browser.new_page()
page.goto("https://example.com", wait_until="domcontentloaded")
page.locator("header").screenshot(path="header.png")
browser.close()
If the selector matches no element, Playwright waits until its timeout and then raises an error. Prefer stable selectors and wait for the component to be visible.
Wait for dynamic content
Navigation completion does not guarantee that client-rendered content, fonts, or images are ready. Wait for a known selector, a short delay, or a suitable load state:
page.goto("https://example.com", wait_until="domcontentloaded")
page.locator("main").wait_for(state="visible")
page.screenshot(path="ready.png")
Use networkidle carefully: analytics, advertisements, and live data can keep a page busy indefinitely. A selector-based wait is usually more predictable.
Async Playwright
In an asyncio application, await navigation, waits, and screenshots consistently:
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as playwright:
browser = await playwright.chromium.launch()
page = await browser.new_page()
await page.goto("https://example.com", wait_until="load")
await page.screenshot(path="async-screenshot.png", full_page=True)
await browser.close()
asyncio.run(main())
See the Playwright Python screenshot guide and Page API for the current method details.
2. Capture the current WebDriver window with Selenium
Install Selenium and make sure a compatible browser driver is available for your environment:
python -m pip install selenium
For a WebDriver session, save_screenshot writes a PNG of the current window. Check its Boolean result so an I/O failure is not silently ignored:
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
driver = webdriver.Chrome(options=options)
try:
driver.get("https://example.com")
if not driver.save_screenshot("screenshot.png"):
raise OSError("Could not save screenshot")
finally:
driver.quit()
Selenium also provides byte and Base64 forms:
png_bytes = driver.get_screenshot_as_png()
with open("screenshot.png", "wb") as output:
output.write(png_bytes)
base64_png = driver.get_screenshot_as_base64()
The documented Selenium Python API describes these methods as screenshots of the current WebDriver window. Do not assume that this API captures the full document; for full-page behavior, use a browser-specific technique or Playwright’s documented full_page option. Consult the Selenium screenshots documentation for current driver behavior.
3. Decide what “open website” means
| Situation | What to do |
|---|---|
| Python created the browser and page | Call Playwright’s page.screenshot or Selenium’s WebDriver screenshot method. |
| You already have a controlled page object | Call the screenshot method on that object. |
| A tab is open in Chrome started separately | Connect through an appropriate browser debugging or WebDriver session first; Python cannot capture an arbitrary desktop tab by URL alone. |
| You need the whole scrollable document | Use Playwright with full_page=True. |
| You need a component only | Use a Playwright locator screenshot. |
| You need to process the image in memory | Use Playwright’s returned bytes or Selenium’s PNG bytes/Base64 methods. |
4. Make captures deterministic
- Set a fixed viewport, device scale factor, and color scheme when visual consistency matters.
- Wait for a stable selector instead of relying on an arbitrary sleep.
- Disable animations in a test-only stylesheet or wait for transitions to finish.
- Use a stable locale, timezone, and test data when the page changes by region or time.
- For lazy-loaded images, scroll or wait for the image element before taking a full-page shot.
- Use an absolute output directory and create it before saving.
from pathlib import Path
from playwright.sync_api import sync_playwright
output = Path("artifacts")
output.mkdir(exist_ok=True)
with sync_playwright() as playwright:
browser = playwright.chromium.launch()
page = browser.new_page(
viewport={"width": 1365, "height": 768},
device_scale_factor=1,
color_scheme="light",
)
page.goto("https://example.com", wait_until="domcontentloaded")
page.locator("main").wait_for(state="visible")
page.screenshot(path=str(output / "page.webp"), type="webp", quality=85)
browser.close()
5. Troubleshooting common errors
| Symptom | Likely cause | Fix |
|---|---|---|
Executable doesn't exist |
Playwright browser binaries were not installed. | Run python -m playwright install chromium. |
TimeoutError while locating an element |
The selector is wrong or the page has not rendered it. | Check the selector, wait for the correct state, and inspect the page at the failure point. |
| Blank or incomplete image | Capture happened before client rendering or lazy loading finished. | Wait for a meaningful selector or image state; avoid an unnecessarily early screenshot. |
| Screenshot file is missing | The directory does not exist or the process lacks write access. | Create the directory, use an absolute path, and check Selenium’s Boolean return. |
| Selenium driver startup failure | Browser and driver setup is incompatible or unavailable. | Install a supported browser, update Selenium, and use the driver’s current setup guidance. |
| Only the visible area was captured | A viewport screenshot was requested. | Use Playwright’s full_page=True; Selenium’s basic method is current-window capture. |
| Fonts or layout differ in CI | Different browser, fonts, viewport, scale factor, or timezone. | Pin the browser environment and explicitly set viewport and rendering-related settings. |
| Navigation hangs | Tracking or streaming requests never become idle. | Use domcontentloaded plus a selector wait instead of waiting forever for network idle. |
6. Performance, reliability, and cost
Launching a browser for every image is slower and uses more memory than reusing one browser process. In a batch job, start one browser, create isolated pages or contexts, and close them when the batch ends. Limit concurrency to what the machine can handle; too many simultaneous pages can cause timeouts and resource pressure.
Full-page images consume more memory than viewport captures. Reduce the viewport, choose WebP or JPEG where lossless PNG is unnecessary, and write bytes directly to storage rather than retaining many images in memory.
For reliable automation, record the URL, viewport, browser version, timestamp, and failure reason alongside each artifact. Retry transient navigation failures with a bounded retry count, but do not retry selector mistakes indefinitely. Treat a successful file write as separate from a successful page load.
Local browser automation has infrastructure costs: browser binaries, CPU, memory, driver maintenance, and time spent handling consent banners, popups, bot checks, and failed pages. A hosted screenshot API can move those concerns out of your worker.
7. Or skip the browser setup
ScreenshotNeo returns a website screenshot with one GET request. Before capture, it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the result with X-Page-Verdict and X-Billed headers.
See the ScreenshotNeo API documentation for request options.
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)
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 also supports full-page capture, CSS-element capture, dark mode, device presets or custom viewports, retina scale, PDF output, custom CSS and JavaScript, clicks, selector waits, delays, network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, caching with a chosen TTL, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. Its MCP server provides take_screenshot, get_page_info, and capture_pdf 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 included on every plan.
Create a free ScreenshotNeo account and get 1,000 screenshots a month without adding a card.
8. FAQ
Can Python screenshot a tab I opened manually?
Not with a normal Playwright or Selenium call. The page must belong to, or be connected through, a browser automation session that Python controls.
Should I choose Playwright or Selenium?
Use Playwright when you want documented page, full-page, and element screenshot workflows with sync or async Python APIs. Use Selenium when your existing test suite already runs through WebDriver.
How do I return an image instead of saving a file?
Call Playwright’s screenshot method without path, or use Selenium’s get_screenshot_as_png() or get_screenshot_as_base64().
Why is my screenshot different on another machine?
Browser version, installed fonts, viewport, device scale, locale, timezone, animations, and network content can all change pixels. Make those inputs explicit when comparing images.
What is the simplest option for scheduled screenshots?
For a small local job, reuse a Playwright browser process. For hosted or batch capture without maintaining browsers, use ScreenshotNeo’s API and its async, caching, and bulk options.


