ScreenshotNeo

BlogComparisons

Best Python Packages for Capturing Website Screenshots with Headless Chrome

Compare Playwright, Selenium, and Pyppeteer for headless Chrome screenshots in Python, with runnable examples for viewport, full-page, and element captures.

By the ScreenshotNeo team4 October 202610 min read

For a new Python screenshot workflow, start with Playwright. Its official Python documentation covers synchronous and asynchronous APIs, viewport and full-page screenshots, screenshots returned as bytes, and screenshots of individual elements. Choose Selenium when your project already uses WebDriver and continuity with that setup matters. Consider Pyppeteer if existing code depends on it, but check current package and Chromium compatibility first: its available reference is for version 0.0.25 and does not establish current maintenance.

These recommendations reflect documented capabilities, not hands-on testing or controlled benchmarks. The sources do not establish which package is fastest, most reliable, or lightest on memory. If you want screenshots without installing or maintaining a browser, ScreenshotNeo is the first hosted alternative to consider: it removes consent banners, popups, and chat widgets before capture, and only clean shots are billed.

Choose a package by screenshot scope and existing stack

Package Documented screenshot capabilities Good fit Important caveat
Playwright for Python Sync and async APIs, viewport and full-page capture, byte output, and locator-based element screenshots. A new workflow that needs several capture modes or a Python-facing browser automation API. Documentation describes features; it does not compare performance or reliability with other packages.
Selenium WebDriver Python can save the current browsing-context screenshot and an individual element screenshot. Chrome options include --headless=new. An existing Selenium/WebDriver project or team using its browser automation model. The cited screenshot example captures the current browsing context. Do not assume that method creates a full-page image.
Pyppeteer Its reference documents PNG/JPEG, full-page, clipping, quality, and encoding controls. Existing code that already depends on its API, after checking compatibility. The available API reference is old (version 0.0.25) and does not establish current maintenance or Chromium compatibility.

For most new code, Playwright is the most straightforward default based on the breadth of the documented screenshot APIs. This is not a speed or reliability ranking. If you need only a viewport image and already run Selenium, its current-context screenshot method may be enough. If you need to screenshot a particular element, Playwright and Selenium both document element capture. For full-page output, Playwright documents an explicit option; Pyppeteer documents a full-page option, while the cited Selenium example does not establish full-page behavior.

Install and run Playwright for Python

Playwright is available in synchronous and asynchronous forms. The examples below use Chromium in headless mode. Install the Python package and its browser binary:

python -m pip install playwright
python -m playwright install chromium

A basic synchronous script navigates to a URL and saves the visible viewport as a PNG:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto("https://example.com", wait_until="load")
    page.screenshot(path="screenshot.png")
    browser.close()

The plain page.screenshot() call captures the page viewport. It does not mean “capture all content below the fold.” Use full_page=True when you need the full scrollable page.

Capture the full page

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page(viewport={"width": 1440, "height": 900})
    page.goto("https://example.com", wait_until="load")
    page.screenshot(path="full-page.png", full_page=True)
    browser.close()

Playwright documents full-page capture and notes that lazy-loaded content may need to be brought into view before capture. A page that loads images or sections only as you scroll can otherwise produce an image with missing content. If the site depends on scrolling to load content, scroll through the page before capturing, then wait for the content you need.

Capture an element

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto("https://example.com", wait_until="load")
    page.locator("main").screenshot(path="main.png")
    browser.close()

Use a locator that uniquely identifies the element you want. If it matches multiple elements or is not visible, refine the selector or wait for the intended element before taking the screenshot.

Use async code or process image bytes

The async API has the same screenshot options. screenshot() returns image bytes when you omit a path, so you can pass the result to another Python library, upload it, or write it yourself.

import asyncio
from pathlib import Path
from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        page = await browser.new_page()
        await page.goto("https://example.com", wait_until="load")
        image_bytes = await page.screenshot(full_page=True)
        Path("full-page.png").write_bytes(image_bytes)
        await browser.close()

asyncio.run(main())

For repeated captures, reuse a browser process and create a fresh page or browser context per task as appropriate for your isolation needs. Close pages, contexts, and the browser when finished so browser processes do not accumulate.

Use Selenium when your project already uses WebDriver

Install Selenium and a compatible Chrome or Chromium setup for your environment. Selenium’s official Chrome documentation shows the --headless=new argument. Selenium Manager can manage drivers in supported configurations; consult the official docs for the environment and version details relevant to your deployment.

python -m pip install selenium

This runnable example captures the current browsing context and saves it as a PNG:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument("--headless=new")

driver = webdriver.Chrome(options=options)
try:
    driver.get("https://example.com")
    driver.save_screenshot("screenshot.png")
finally:
    driver.quit()

save_screenshot() captures the current browsing context shown in Selenium’s example. Treat it as a viewport/current-context capture; the cited example does not establish full-page capture. Selenium also documents taking a screenshot from an individual element:

element = driver.find_element("css selector", "main")
element.screenshot("main.png")

Run the element example after navigation and after the element is present and visible. For dynamic pages, use an explicit wait for the expected element rather than relying on a fixed sleep.

Consider Pyppeteer only with a compatibility check

Pyppeteer describes itself as an unofficial Python port for headless Chrome/Chromium. Its API reference documents PNG/JPEG output, full-page screenshots, clip rectangles, quality, and encoding options. The reference is for version 0.0.25, and the available sources do not establish current maintenance or compatibility with current Chromium releases. That makes it a more cautious choice for a new workflow than Playwright.

The API documentation shows controls such as fullPage, clip, type, quality, and encoding. Confirm the installed package’s current API, supported Python version, and compatible browser before relying on an example. Do not select it on the assumption that it tracks current Chrome releases.

Screenshot choices that affect the result

Need Playwright approach Notes
Visible viewport page.screenshot(path="shot.png") Uses the page viewport dimensions. Set viewport size when a consistent layout matters.
Full scrollable page page.screenshot(path="shot.png", full_page=True) Useful for long pages. Lazy content may require scrolling or other page-specific preparation first.
One element page.locator(".card").screenshot(path="card.png") Use a stable selector and ensure the element is visible.
Image bytes image_bytes = page.screenshot() Omit path to receive bytes for further processing or upload.
Consistent viewport browser.new_page(viewport={"width": 1440, "height": 900}) Viewport dimensions can change responsive layout and what appears in a viewport capture.

Playwright’s cited guide establishes the screenshot modes above. For output format and additional screenshot options, consult the relevant package reference rather than assuming an option or default. Selenium’s cited examples establish current-context and element screenshots; Pyppeteer’s reference documents additional format and clipping controls.

Wait for the page you actually need

Navigation completing does not guarantee that every application has finished rendering. Choose a readiness condition tied to the content you need: wait for a known selector, use a suitable navigation wait condition, or wait for a specific application state. Avoid treating an arbitrary delay as proof that a page is ready. Network-idle behavior can also be unsuitable for pages that keep requests open or poll continuously.

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto("https://example.com", wait_until="domcontentloaded")
    page.locator("main").wait_for(state="visible")
    page.screenshot(path="ready.png")
    browser.close()

For a page that loads images lazily, scroll through the content before taking a full-page screenshot and wait for relevant images to load. This is site-dependent: there is no universal readiness signal for every page.

cURL, Python, and Node.js alternatives with ScreenshotNeo

If the task is to get a screenshot rather than operate a local browser, ScreenshotNeo provides a one-request website screenshot API. It returns an image such as PNG, JPEG, or WebP, or a PDF. The parameter names used by other screenshot APIs also work, which can simplify switching. See the ScreenshotNeo API documentation for options and request 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}`);

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses identify the page verdict and billing status in headers. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools 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 for 1,000 free screenshots a month with no card.

Troubleshooting

Symptom Likely cause What to try
Playwright cannot launch Chromium The browser binary is not installed in the environment where the script runs, or the runtime cannot access required browser dependencies. Run python -m playwright install chromium in the deployment environment and follow the official installation guidance for its operating system and dependencies.
The screenshot is blank or shows a loading state The capture ran before the page’s relevant content rendered, navigation failed, or the site returned a blank/error page. Check the page URL and navigation outcome, then wait for a selector or state that indicates the content is ready. Capture diagnostics when handling failures.
Content is missing below the fold A viewport screenshot was used, or the page only loads content while scrolling. Use Playwright’s full_page=True for full-page capture and scroll to trigger lazy loading before capture when needed.
An element screenshot fails The selector matches no element, matches ambiguously, or the element is not visible. Use a stable, specific locator; wait for the intended element to be visible; confirm it exists on the page.
Selenium cannot start Chrome Chrome/Chromium, driver setup, runtime dependencies, or headless configuration are incompatible with the environment. Check the installed browser and Selenium setup, use the documented headless option for Chrome, and review Selenium’s current Chrome guidance for the environment.
The output differs across runs Responsive layout, fonts, animations, timestamps, ads, dynamic content, or asynchronous loading changed between captures. Fix viewport dimensions and wait for a meaningful page state. Where the page allows it, disable or stabilize animations and dynamic inputs.
Pyppeteer breaks after a browser update The old reference does not establish compatibility with current Chromium. Check the package and browser versions and compatibility before upgrading; for a new workflow, consider a package with current documented support for the needed behavior.

Performance, reliability, and cost

The cited sources do not provide controlled benchmarks, so there is no evidence here to rank these packages by speed, memory use, or reliability. Local browser capture requires installing and operating a browser runtime, and your workload and page behavior will affect resource use. For repeated work, avoid launching an entirely new browser for every URL when reuse is safe; close pages and browser processes deliberately, and isolate jobs when cookies or session state must not be shared.

Reliability depends on more than the package: browser/package compatibility, network conditions, page readiness, bot defenses, and dynamic site behavior all matter. Pin and update dependencies deliberately, and validate screenshots against the states your application cares about. Local package use has no per-screenshot API price in the cited sources, but infrastructure and engineering costs are not quantified here. ScreenshotNeo offers 1,000 shots per month free, then paid tiers of $5 for 3,000, $15 for 15,000, $39 for 60,000, $99 for 250,000, and $249 for 1,000,000; yearly billing gives two months free.

FAQ

Which package should I choose for a new Python project?

Playwright is the documented-capability default here because its Python guide covers sync, async, full-page, byte, and element screenshots.

Can Selenium take a full-page screenshot?

The cited Selenium example shows a current browsing-context screenshot and an element screenshot. It does not establish full-page support for that method.

Does headless mode change which package is best?

It does not change the selection criteria: choose based on required screenshot scope, API style, and your existing automation stack, then verify browser compatibility.

Is Pyppeteer faster than Playwright?

The reviewed sources contain no controlled comparison that establishes a speed ranking.

How do I capture a page without managing Chrome?

Use a screenshot API such as ScreenshotNeo when you need a hosted capture endpoint instead of a local browser process.

Sources