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

To save HTML as a PNG in Python, render it in a real browser and take a screenshot. Playwright is a straightforward choice: its Python API can capture the visible viewport, the full scrollable page, or a single element, and it can return PNG bytes instead of writing directly to a file.

Use Playwright to render HTML and save a PNG

A browser engine is important because HTML may rely on CSS, fonts, images, and JavaScript to look as intended. Playwright launches Chromium, loads the page, then saves the rendered result with page.screenshot().

Install Playwright and its browser

Install the Python package and Chromium in the environment where the script will run:

  1. python -m pip install playwright
  2. python -m playwright install chromium

Save the following as save_html_png.py. It captures a webpage at a fixed viewport and writes a full-page PNG:

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.
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})
    page.goto("https://example.com", wait_until="networkidle")
    page.screenshot(path="page.png", full_page=True)
    browser.close()

Run it with python save_html_png.py. The browser needs to be installed and available in that runtime; if this is a server or container, install Chromium there too. The viewport sets the browser’s visible width and height in CSS pixels. With full_page=True, Playwright captures the full scrollable document rather than only that visible area.

Capture a local HTML file

For a file on disk, convert its resolved path to a file: URL. This avoids problems caused by passing a relative path:

from pathlib import Path
from playwright.sync_api import sync_playwright

html_file = Path("page.html").resolve()

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page(viewport={"width": 1440, "height": 900})
    page.goto(html_file.as_uri())
    page.screenshot(path="page.png", full_page=True)
    browser.close()

Relative resources referenced by the HTML, such as images or stylesheets, must also be available at the locations the page expects. If the page fetches remote resources, the runtime needs network access to those resources.

Choose what part of the page to capture

Goal Playwright call What it captures
Visible browser area page.screenshot(path="viewport.png") The current viewport, using the page’s configured viewport dimensions.
Entire document page.screenshot(path="full.png", full_page=True) The full scrollable page, including content below the fold.
One element page.locator(".invoice").screenshot(path="invoice.png") The selected element, useful for a card, invoice, chart, or other component.
Image bytes in memory png_bytes = page.screenshot() Image bytes that can be written to a file or passed to another image-processing step.

For an element capture, choose a selector that identifies the intended element. If the selector matches nothing, the capture cannot be made; if it matches multiple items, make the target unambiguous. Playwright also supports animations="disabled" on screenshot calls, which can make captures more repeatable when page animations would otherwise change what appears in the image.

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.

Write returned bytes to a file

Omit path when you want the screenshot bytes in Python. Then save, upload, or transform them as needed:

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": 1440, "height": 900})
    page.goto("https://example.com", wait_until="networkidle")
    png_bytes = page.screenshot(full_page=True)
    Path("page.png").write_bytes(png_bytes)
    browser.close()

When you do provide a path, Playwright infers the screenshot type from the filename extension. A .png path selects PNG output. The API also supports JPEG and WebP; PNG is lossless, so Playwright’s screenshot quality setting does not apply to PNG captures.

Wait for the right page state before capturing

A screenshot records what the browser has rendered at capture time. A page can reach its initial load state before a particular image, chart, or client-rendered component is ready. Wait for the thing the image depends on rather than adding an arbitrary sleep.

Wait for a required element

If a known element signals that the page is ready, wait for it before taking the screenshot:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
page.goto("https://example.com/report")
page.locator(".report-ready").wait_for()
page.screenshot(path="report.png", full_page=True)

Replace .report-ready with a selector that exists only when the content you need is ready. For pages whose state is better represented by a specific change, wait for that state instead.

Choose a navigation wait condition deliberately

The example uses wait_until="networkidle". This can be convenient for pages that become quiet after loading, but it is not a universal readiness test: analytics, polling, or other ongoing network activity may prevent a page from becoming idle. If that happens, navigate without relying on network idle and wait for the relevant selector or state. Conversely, if the page’s important content loads after navigation, a screenshot taken immediately can be incomplete.

Use Selenium if it is already your project standard

Selenium’s Python WebDriver can save the current window as a PNG or return its PNG bytes. It is a reasonable fit when your project already uses Selenium for browser automation:

from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
driver = webdriver.Chrome(options=options)
try:
    driver.get("https://example.com")
    driver.save_screenshot("page.png")
finally:
    driver.quit()

driver.save_screenshot("page.png") saves the current window, and driver.get_screenshot_as_file("page.png") is another file-saving method. To handle the PNG in memory, use driver.get_screenshot_as_png(). Selenium’s documented core screenshot methods focus on the current window and PNG files or bytes. For a direct full-page option and locator-based element screenshot, Playwright exposes full_page=True and locator screenshots in its page API.

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

Make captures consistent and manageable

  • Set the viewport explicitly. Layouts can change at different widths, so use the same viewport for captures that need to be comparable.
  • Wait for the actual content. Use a selector or page state tied to the material that must appear; fixed delays can be too short on a slow run and unnecessarily long on a fast one.
  • Make fonts and network resources available. Missing fonts, stylesheets, or images can alter the rendered result even when the screenshot call succeeds.
  • Consider the output dimensions. A full-page capture of a long document can produce a very tall image. Capture a specific element or separate sections if a single document-sized PNG is impractical for the next step.
  • Control animation where appropriate. Disabling animations can reduce variation in element captures, though it does not replace waiting for data or other content to load.

These are practical implementation considerations, not performance benchmarks. Runtime and output size depend on the page, browser environment, resources, and capture dimensions; no general timing or file-size figure applies to every page.

Or skip the browser setup

If you would rather call a screenshot service than install and manage a browser, ScreenshotNeo accepts a URL and returns a screenshot as PNG, JPEG, or WebP, or a PDF. Here is the one-request cURL example; see the ScreenshotNeo API documentation for its request options:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo’s cleanup options accept cookie or consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets before capture; 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 report the page verdict and billing status. An MCP server offers take_screenshot, get_page_info, and capture_pdf tools for AI agents 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. Every feature is available on every plan.

Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month without a card.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting

Playwright reports that Chromium is missing

The Python package and browser runtime are separate pieces. Install Chromium in the same environment that runs the script with python -m playwright install chromium. In a deployment, container, or virtual environment, make sure the install step runs there too.

The PNG is blank or missing part of the page

Check that the target URL or local file loaded and that the content exists before capture. Wait for a selector that marks the required content as ready. For a page that keeps network connections open, do not rely solely on networkidle; use a content-specific wait instead.

Images or fonts are absent

Verify that linked resources can be reached from the browser environment and that local relative paths resolve from the HTML file’s location. A screenshot only reflects what the browser could load and render.

The output includes only the top of the page

The default screenshot is the viewport. Add full_page=True for the complete scrollable document, or take a locator screenshot if only a particular component is required.

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

An element screenshot fails or targets the wrong item

Confirm that the locator matches the intended element and that it is present and visible before capture. Use a more specific CSS selector when a page contains several elements with the same class.

Selenium does not produce a complete long-page image

Selenium’s core screenshot methods capture the current window. If you need a documented direct full-page call, use Playwright’s full_page=True; otherwise, full-page behavior with Selenium may require browser-specific techniques or stitching.

Which Python approach should you choose?

For a new script that needs a viewport, full-page, or element PNG, start with Playwright’s Python API. Choose Selenium when WebDriver is already established in your project and a current-window screenshot is sufficient. In either case, render the HTML in the browser engine, wait for the content that matters, and pick the capture scope that matches the image you need.

Frequently Asked Questions

Can Playwright return a screenshot without creating a file?

Yes. Call page.screenshot() without a path to receive image bytes, then write or pass those bytes to another part of your Python program.

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

Can I save the screenshot as JPEG or WebP instead of PNG?

Yes. Playwright supports PNG, JPEG, and WebP; when saving to a path, the filename extension determines the output type.

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.