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.
Recommended Free Tools
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:
#1 Best Overall
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:
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchespyppeteer-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 afinallyblock 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:
Rank #2
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.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →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:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →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.
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.
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.
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.
Best Value
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.
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.
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.
Quick Recap
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.

