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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Pyppeteer lets Python code drive Chromium with an asynchronous, Puppeteer-like API. Install it with python -m pip install pyppeteer, optionally pre-download its browser with pyppeteer-install, then launch a browser, open a page, perform actions, and close the process. However, Pyppeteer is an unofficial port rather than the official JavaScript Puppeteer project, and its own README now describes the repository as unmaintained. Treat it as a compatibility or legacy choice; for new automation, evaluate Playwright Python as well.

What Pyppeteer is—and what it is not

Pyppeteer is a Python port of Puppeteer for automating Chrome or Chromium. It follows the same general model—launch a browser, create a page, navigate, query the DOM, evaluate JavaScript, and capture output—but the APIs are adapted to Python. It is not the official Puppeteer package, which is a JavaScript library, and JavaScript examples are not automatically valid Python.

The project README currently warns that the repository is unmaintained and recommends considering Playwright Python. PyPI lists Pyppeteer 2.0.0, released February 18, 2024, with Python 3.8 or newer and below Python 4.0. Those facts matter when deciding whether to introduce Pyppeteer into a new production system: test the exact Python version, operating system, container image, browser binary, and network policy you will deploy.

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

Install Pyppeteer on a supported Python version

1. Create an isolated environment

Using a virtual environment prevents Pyppeteer and its dependencies from changing system-wide packages:

python -m venv .venv
# macOS/Linux
source .venv/bin/activate
# Windows PowerShell
.venvScriptsActivate.ps1

Confirm that the interpreter is Python 3.8 or later:

python --version

2. Install the package

python -m pip install pyppeteer

Pyppeteer may download a compatible Chromium build the first time your code launches a browser if it cannot find a suitable local executable. The project README gives an approximate download size of 150 MB; the actual size depends on platform and the browser revision.

3. Make the browser download an explicit setup step (optional)

In a build image or a controlled deployment, you may prefer to download the browser before running application code:

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

Keep the resulting cache available to the account that runs your program. If your environment already manages Chrome or Chromium, configure the executable path for that machine rather than assuming one path works on every operating system or container.

Your first Pyppeteer script: navigate and take a screenshot

Save this as capture.py:

import asyncio
from pyppeteer import launch

async def main():
    browser = await launch()
    page = await browser.newPage()
    await page.goto("https://example.com")
    await page.screenshot({"path": "example.png"})
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())

Run it with:

python capture.py
  • launch() starts the browser process. It returns an awaitable browser object.
  • newPage() creates a tab.
  • goto() navigates that tab to the URL.
  • screenshot() writes the captured image to the path supplied in the options dictionary.
  • close() shuts down Chromium. Put cleanup in a finally block in long-running or error-prone programs so a failed task does not leave browser processes behind.

Navigation completion is not the same as “every application task is finished.” Single-page applications may continue rendering after the initial response. Use an explicit selector, a delay, or another application-specific readiness check when the page needs more time.

Launch options and page setup

Pyppeteer accepts keyword arguments as well as option dictionaries. For example, these are equivalent in intent:

browser = await launch(headless=True)
# or
browser = await launch({"headless": True})

Headless mode is convenient for servers. For local debugging, launch in a visible mode where supported by your environment and add a slow-motion delay or pauses in your own code. Browser executable paths, sandbox flags, proxy settings, and display requirements vary by operating system and container; only add environment-specific launch arguments when your deployment requires them, and test the security implications of disabling a sandbox.

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

A useful page setup before navigation can set the viewport:

await page.setViewport({"width": 1440, "height": 900, "deviceScaleFactor": 1})
await page.goto("https://example.com", {"waitUntil": "networkidle2"})

The exact wait condition should match the site. A network-idle condition can be unsuitable for pages with analytics, streams, or long polling; waiting for a known element is often more deterministic.

Find elements and perform actions

JavaScript Puppeteer uses symbols such as $, $$, and $x. Python cannot use those names as identifiers, so Pyppeteer provides Python-friendly methods:

# CSS selector: one element
button = await page.querySelector("button[type='submit']")

# CSS selector: all matching elements
cards = await page.querySelectorAll(".card")

# XPath selector
heading = await page.xpath("//h1")

Always handle a missing element before calling methods on it:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
submit = await page.querySelector("button[type='submit']")
if submit is None:
    raise RuntimeError("Submit button was not found")
await submit.click()

Typical interaction flow:

await page.type("input[name='q']", "pyppeteer")
await page.click("button[type='submit']")
await page.waitForSelector("main.results")
text = await page.querySelectorEval("main.results", "el => el.innerText")
print(text)

Use selectors that describe stable application semantics—an accessible label, a data attribute, or a meaningful class—rather than a generated framework class that may change between builds.

Evaluate JavaScript in the page

Pyppeteer documents page.evaluate for running JavaScript in the page context. Pass a JavaScript expression or function as a string:

title = await page.evaluate("document.title")
links = await page.evaluate("Array.from(document.querySelectorAll('a')).map(a => a.href)")
print(title)
print(links)

When an expression is mistaken for a function, the documentation advises trying force_expr=True:

width = await page.evaluate("window.innerWidth", force_expr=True)

Page JavaScript runs with the page’s permissions and state. Do not treat values returned from an untrusted page as safe input to shell commands, database queries, or HTML templates without validation.

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

Capture full pages, elements, and PDFs

Full-page image

await page.screenshot({
    "path": "full-page.png",
    "fullPage": True,
    "type": "png"
})

Full-page capture can trigger lazy-loaded content only if scrolling or another interaction causes the site to load it. If the page uses lazy images, scroll through it first and wait for the image requests before capturing.

One element

panel = await page.querySelector(".invoice")
if panel is None:
    raise RuntimeError("Invoice panel not found")
await panel.screenshot({"path": "invoice.png"})

PDF output

await page.pdf({
    "path": "page.pdf",
    "format": "A4",
    "printBackground": True,
    "margin": {"top": "16mm", "right": "16mm", "bottom": "16mm", "left": "16mm"}
})

PDF rendering depends on the Chromium revision and the page’s print styles. Set a viewport and load fonts before capture when pagination or typography must be consistent.

Write a production-friendly async workflow

Close resources even when navigation or an assertion fails:

import asyncio
from pyppeteer import launch

async def capture(url: str, output: str):
    browser = await launch()
    try:
        page = await browser.newPage()
        await page.setViewport({"width": 1365, "height": 768})
        await page.goto(url, {"waitUntil": "domcontentloaded", "timeout": 60000})
        await page.waitForSelector("body", {"timeout": 30000})
        await page.screenshot({"path": output, "fullPage": True})
    finally:
        await browser.close()

asyncio.get_event_loop().run_until_complete(
    capture("https://example.com", "example-full.png")
)
  • Set explicit navigation and selector timeouts appropriate to your service-level needs.
  • Retry only failures that are plausibly transient, such as a temporary network reset; repeated retries can multiply load on a target site.
  • Use a separate browser context or browser process for jobs that must not share cookies and local storage.
  • Pin and test dependency versions in deployment rather than allowing an unattended browser revision change.
  • Respect the target site’s terms, authentication boundaries, robots policy where applicable, and rate limits.

Troubleshooting common failures

“No module named pyppeteer”

The package was installed into a different interpreter or virtual environment. Activate the environment and run python -m pip show pyppeteer; invoke the script with that same python.

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.

Chromium download fails or launch cannot find a browser

Check outbound network access, write permissions for the browser cache, and available disk space. Run pyppeteer-install during image creation, or configure a known local Chrome/Chromium executable for the deployment. Do not assume a cache created under one user is readable by another.

The page times out

Verify DNS, proxy and firewall settings, then choose a wait condition that fits the page. A site with persistent connections may never become network-idle; use domcontentloaded followed by waitForSelector for the element your workflow actually needs.

A selector returns None or an empty list

The element may be inside an iframe, rendered later, hidden behind a consent dialog, or addressed by an unstable selector. Wait for a stable selector, inspect frames, and check the spelling and quoting of the CSS or XPath expression.

evaluate raises an error

Remember that the string is JavaScript executed in the browser, not Python. Return serializable values, quote JavaScript correctly inside the Python string, and try force_expr=True when an expression is classified incorrectly.

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

The screenshot is blank or incomplete

Wait for the application’s content and fonts, scroll to trigger lazy loading, and verify that the selected element has non-zero dimensions. A page can load successfully while its meaningful content is still being assembled by JavaScript.

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

Pyppeteer or Playwright Python?

There is no source-backed universal speed or reliability winner, so choose against your requirements rather than an assumed benchmark.

Decision point Pyppeteer Playwright Python
Project status Unofficial Puppeteer port; the project README says it is unmaintained. Official Python documentation and release-linked browser support.
Installation pip install pyppeteer; Chromium may download on first launch, or use pyppeteer-install. pip install playwright, followed by playwright install.
Browser choices documented by the project Chromium-focused workflow. Chromium, Firefox, and WebKit launch options.
API style Asynchronous Python methods modeled on Puppeteer, with Python selector names. Both synchronous and asynchronous Python APIs.
Best fit Existing Pyppeteer code or a controlled compatibility task. New work where active maintenance and multiple browser engines matter.

Playwright’s browser binaries are tied to Playwright releases. After updating the Python package, you may need to run its browser installation command again. Whichever tool you select, exercise the exact versions and deployment constraints used in production.

Or skip the browser setup

If your goal is a dependable screenshot rather than maintaining a local automation runtime, ScreenshotNeo provides a website screenshot API and MCP server. It removes cookie-consent banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

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

See the parameter reference in the ScreenshotNeo documentation. A single request can return PNG, JPEG, WebP, or PDF:

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}`);

It also supports full-page captures with lazy images loaded, CSS-selector element capture, device presets and custom viewports, dark mode, retina scale, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. Plans include 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000 screenshots, and every feature is available on every plan. Create a free ScreenshotNeo account to try it.

Frequently Asked Questions

Can Pyppeteer automate a site that requires Firefox or WebKit?

The documented Pyppeteer workflow is for Chrome/Chromium. If your test matrix requires Firefox or WebKit, evaluate Playwright Python, whose documentation includes launch options for all three engines.

Should I commit Pyppeteer’s downloaded Chromium into my repository?

Usually no. Download it during environment or image setup and retain the cache in that deployment, or point Pyppeteer at a browser installed and managed by your platform.

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.

Why does a network-idle wait never finish?

Analytics, WebSockets, streaming, and long-polling requests can keep a page active indefinitely. Use a concrete readiness selector or an application-specific condition instead.

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.