Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For browser-faithful HTML screenshots in Python, start with Playwright. It renders pages in a real Chromium-based browser, supports viewport, full-page, and element captures, and can save PNG, JPEG, or WebP files or return image bytes. Choose html2image for a smaller fixed-size wrapper around Chrome, and choose WeasyPrint when your actual deliverable is a print-oriented PDF rather than a direct raster image.

This guide compares the three libraries, shows runnable Python code, explains browser setup and security boundaries, and gives a hosted option when you do not want to maintain browser binaries.

Which Python library should you choose?

Library Best fit Input and output Important constraints
Playwright for Python Browser-rendered screenshots, full pages, elements, and controlled automation URLs, local pages, or generated HTML loaded in a browser; PNG, JPEG, WebP, or bytes Install the Python package and compatible browser binaries
html2image Simple fixed-size captures from HTML/CSS strings, files, or URLs HTML/CSS, files, and URLs rendered through headless Chrome/Chromium Requires a supported browser; its documented API does not request full-page screenshots; process only trusted content
WeasyPrint Print layout and paginated documents HTML and CSS to PDF Raster output needs a separate PDF-to-image conversion stage

These are different workflows, not interchangeable implementations. Decide whether you need JavaScript execution, a full scrolling document, a single element, exact print pagination, or a direct image file before selecting a package.

1. Playwright: the default for real web pages

Playwright is the strongest general-purpose choice when “convert HTML to an image” means “show me what this page looks like in a browser.” Its Python API exposes page screenshots, full-page screenshots, and locator (element) screenshots. You can write the result to a file or receive bytes for further processing.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Install the package and browser

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

The second command matters in CI, containers, and new virtual environments: installing the Python package alone does not install the browser executable that Playwright launches. Pin your Python dependency and browser installation in deployment, then test after upgrades because browser rendering can change.

Capture a viewport screenshot

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page(viewport={"width": 1440, "height": 900}, device_scale_factor=1)
    page.goto("https://example.com", wait_until="networkidle")
    page.screenshot(path="viewport.png", type="png")
    browser.close()

wait_until="networkidle" waits for network activity to settle, but it is not a guarantee that every application has finished rendering. For a known application state, wait for a selector or use an explicit short delay after the selector appears.

Capture the complete scrollable page

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page(viewport={"width": 1365, "height": 900})
    page.goto("https://example.com", wait_until="domcontentloaded")
    page.screenshot(path="full-page.webp", full_page=True, type="webp", quality=85)
    browser.close()

Use full_page=True when the output must include content below the fold. Pages with lazy-loaded images may need scrolling first so those images are requested before capture.

Capture one element

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")
    card = page.locator(".pricing-card").first
    card.wait_for(state="visible")
    card.screenshot(path="pricing-card.png")
    browser.close()

Locator screenshots avoid stitching unrelated page content. They are useful for product cards, charts, invoices, and social previews whose dimensions should follow the element itself.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Return bytes instead of writing a file

from pathlib import Path
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page(viewport={"width": 1200, "height": 630})
    page.goto("https://example.com", wait_until="networkidle")
    image_bytes = page.screenshot(type="png")
    Path("preview.png").write_bytes(image_bytes)
    browser.close()

Bytes let you upload directly to object storage, pass an image to another service, or run transformations without a temporary screenshot file.

Use the asynchronous API

import asyncio
from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        page = await browser.new_page(viewport={"width": 1440, "height": 900})
        await page.goto("https://example.com", wait_until="domcontentloaded")
        await page.locator("main").wait_for(state="visible")
        await page.screenshot(path="async.png", full_page=True)
        await browser.close()

asyncio.run(main())

The async API fits an application that already uses asyncio. Do not create a new browser for every URL in a large batch; keep one browser process, create isolated contexts or pages, and close pages deterministically.

Control common rendering differences

  • Set viewport and device_scale_factor explicitly for reproducible dimensions.
  • Set a page color scheme or emulate a device when your CSS changes for dark mode or mobile breakpoints.
  • Wait for a specific selector, image, font, or application state instead of relying only on a timer.
  • For infinite scroll, programmatically scroll in increments, wait for new content, then capture.
  • Keep credentials and private URLs out of screenshots and logs; browser contexts can carry cookies and authorization.

2. html2image: a small wrapper for straightforward captures

html2image is convenient when you have a short HTML/CSS string, a local file, or a URL and want a fixed-size image with minimal browser automation. It wraps headless Chrome or Chromium, so that browser must be installed and discoverable.

Install and capture HTML

python -m pip install html2image
from html2image import Html2Image

html = """
<!doctype html>
<html>
  <body style='margin:0;background:#111;color:white;font:32px sans-serif;padding:40px'>
    Rendered with html2image
  </body>
</html>
"""

hti = Html2Image(output_path="captures", size=(1200, 630))
hti.screenshot(html_str=html, save_as="card.png")

The documented default capture size is 1920 by 1080, but set size explicitly for social cards, thumbnails, or test fixtures. You can also pass a URL or file according to the package API.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Know the full-page and security limits

html2image’s documented interface does not provide a full-page screenshot request. If the page can extend beyond one viewport, use Playwright instead. The project also warns that unsanitized input can lead to malicious code execution. Treat HTML, CSS, scripts, and URLs as trusted only; isolate rendering of user-submitted content and do not run it with production credentials or broad filesystem access.

3. WeasyPrint: choose it for PDF-first print rendering

WeasyPrint is a document renderer, not a browser screenshot API. Its workflow is HTML and CSS to PDF, with print-oriented pagination, paper sizes, margins, and page breaks. That is appropriate for reports, invoices, and forms where a PDF is the primary artifact.

from weasyprint import HTML

HTML(string="""
<html>
  <body>
    <h1>Monthly report</h1>
    <p>Print-oriented content.</p>
  </body>
</html>
""").write_pdf("report.pdf")

WeasyPrint is not evidenced here as a direct page-to-PNG/JPEG/WebP API. If an image is required, add and validate a PDF rasterization step with a separate tool. This introduces another dependency and can affect text sharpness, page dimensions, and color handling.

How to choose by requirement

  • JavaScript-heavy website: Playwright. A browser executes scripts and exposes browser-like waiting and interaction.
  • Entire long page: Playwright with full_page=True, plus scrolling for lazy content.
  • One component: Playwright locator screenshot.
  • Small static HTML snippet: html2image, if a single fixed viewport is enough.
  • Print pages or PDF delivery: WeasyPrint.
  • Image bytes for a pipeline: Playwright’s screenshot return value.
  • Untrusted user HTML: isolate the renderer; html2image’s security warning makes this boundary especially important.

Or skip the browser setup

ScreenshotNeo is a hosted website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or a PDF, while the service accepts cookie/consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Each step can be disabled.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Response headers identify the page verdict and whether it was billed. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

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(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

See the ScreenshotNeo documentation for parameters. It includes full-page and lazy-image loading, CSS-selector element capture, dark mode, 12 device presets or custom viewports, retina scale, PDF paper and page controls, custom CSS and JavaScript, clicks, selector or network-idle waits, request/resource blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of 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.

The Free plan includes 1,000 shots each month with no card. Paid plans start at $5 for 3,000 shots; higher plans are 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. Create a free ScreenshotNeo account to start.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting

Playwright says the browser executable is missing

Run python -m playwright install chromium in the same environment that runs your script. In containers, install the browser during image build and ensure required system dependencies are present.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The screenshot is blank or captures a loading shell

Wait for a meaningful selector, not just navigation. Check that the selector is visible, allow fonts and images to load, and use a bounded timeout. Some sites require authentication, consent interaction, or a longer application-specific state transition.

Lazy images are absent from a full-page shot

Scroll through the page before capture and wait after each batch of content. Confirm image URLs return successfully and that an intersection-observer component has had time to render.

Dimensions differ between runs

Set viewport, device scale factor, browser version, fonts, and color scheme explicitly. Avoid animations by injecting CSS that disables transitions when visual determinism matters.

html2image cannot find Chrome

Install a supported Chrome or Chromium build and configure the package with its executable path if it is outside the normal search locations. Verify the same user or container can launch it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

PDF output has unexpected page breaks

That is a print-layout issue rather than a screenshot issue. Define page size, margins, and CSS print rules in WeasyPrint; if the final format is raster, inspect the added PDF conversion stage for scaling and clipping.

Performance, reliability, and cost considerations

No fair speed or fidelity ranking is established by the documentation for these packages. Performance depends on page complexity, network conditions, browser startup, fonts, images, and your capture dimensions. Reuse a Playwright browser, cap concurrency to available CPU and memory, and apply explicit navigation and screenshot timeouts. Cache stable pages when acceptable, but invalidate the cache when content freshness is part of the requirement.

For self-hosted libraries, budget for browser binaries, operating-system dependencies, patching, isolation, and observability. For a hosted API, compare billing semantics as well as headline quotas: ScreenshotNeo reports whether a response was billed and does not bill the listed failed-load categories.

FAQ

Can Python take a screenshot of an HTML page?

Yes. Playwright launches a browser and captures a page or element directly; html2image provides a simpler Chrome wrapper for fixed-size captures.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Does WeasyPrint convert HTML directly to PNG?

Its documented workflow is HTML to PDF. A separate rasterization step is required for PNG, JPEG, or WebP output.

Which option is best for a long, JavaScript-rendered page?

Playwright, because it combines browser execution with full-page capture and selector-based waiting.

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.