Puppeteer vs Playwright: Which One for Scraping

Puppeteer vs Playwright, measured on this machine today. Install sizes, a head-to-head scrape, auto-wait, engine coverage, and the stealth-plugin gap.

MMotherlode Lab8 min read
Magpie title card: Puppeteer vs Playwright: Which One for Scraping, over a storefront's HTML source

Puppeteer vs Playwright is a family feud. Playwright’s first engineers built Puppeteer at Google, then moved to Microsoft and rebuilt the idea with the complaints fixed, and both shipped a release two days before I wrote this. I installed both fresh on this machine today and ran the same scrape through each, so the numbers below are measured, not quoted.

The verdict

Pick Playwright if you scrape more than Chrome-only targets, write Python as well as JavaScript, or have ever shipped a scraper that grabbed a half-rendered page because an animation was still running. Pick Puppeteer if your whole pipeline is Node, your targets are Chrome, and you need the PDF generator or raw CDP access. Neither is wrong. One is a wider tool; the other is a tighter one.

Puppeteer 25.14.0 Playwright 1.64.1
Maintainer Google’s Chrome DevTools team Microsoft
License Apache-2.0 Apache-2.0
GitHub stars (Oct 16, 2026) 95,705 97,852
Languages JavaScript / TypeScript JS/TS, Python, Java, .NET
Engines Chrome/Chromium; Firefox via WebDriver BiDi, no WebKit Chromium, Firefox, WebKit
Browser download (measured) 669 MB at npm install 681 MB for Chromium only, ~1.3 GB with all three
Auto-wait on actions No, you write the waits Yes, on every action
Protocol Chrome DevTools Protocol Playwright protocol per engine
PDF generation First-class Chromium only
Stealth plugins on npm puppeteer-extra 3.3.6 + stealth 2.11.2 None maintained (playwright-stealth 0.0.1, 2021)
Latest release Oct 14, 2026 Oct 14, 2026

Puppeteer vs Playwright at the terminal

Both installs happened today on this WSL2 box, Node 24.2.0, home connection. Your numbers will drift; the ratio will not.

What each install downloads

npm i puppeteer took 1 minute 31 seconds and left 33 MB of node_modules plus 669 MB in ~/.cache/puppeteer. The package downloads two Chrome builds at install time: a full Chrome for Testing (399 MB) and a stripped chrome-headless-shell (270 MB), both at 155.0.8059.79. You pay the disk cost up front whether you wanted the second build or not.

npm i playwright took 2 seconds and downloaded nothing. That is not savings; it is a deferred charge. The browsers come when you run npx playwright install chromium, which took 2 minutes 1 second and wrote 681 MB here. Playwright 1.64 pins Chromium revision 1248 (Chrome 156.0.8078.4), and the same command can also fetch Firefox and WebKit, pushing the cache past 1.3 GB. On this minimal WSL setup, the WebKit download succeeded and then failed host validation, with Playwright naming the missing system libraries exactly: libevent, five gstreamer pieces, libflite, libavif. One sudo apt-get fixes it. Budget for that on bare servers.

Same script, same selectors

Then the same job through both. Ten quotes from quotes.toscrape.com/js/, a page that renders its content with JavaScript, so static HTTP gets nothing. Twelve lines of code each, same logic, same selectors:

// driver picked by argv; imports differ, everything else identical
const page = await browser.newPage();
await page.goto('https://quotes.toscrape.com/js/', { waitUntil: 'networkidle' });
const quotes = await page.$$eval('.quote .text', els => els.map(e => e.textContent));

Puppeteer pulled all 10 quotes in 3,189 ms and 3,053 ms across two runs, peak single-process RSS around 203 MB. Playwright did it in 2,707 ms and 2,369 ms at around 173 MB. Both scripts worked on the first try. One page from one connection is a data point, not a benchmark; treat the gap as noise and the identical selector syntax as the real finding.

Where Playwright pulls ahead

Auto-wait is the difference you feel every day. Playwright’s click and fill calls wait for the element to exist, be visible, and be stable before acting. Puppeteer’s equivalent calls throw if the element is not there yet, so you assemble waitForSelector around everything, and sites that hydrate late still find the gaps. Most “my scraper worked yesterday” bugs in this family are races, and Playwright removed the race by default.

Engine coverage is the second gap. Some sites serve different markup to WebKit, and any claim that your output matches what Safari users see needs a WebKit render. Playwright runs Chromium, Firefox, and WebKit from one API. Puppeteer is Chrome or nothing: Firefox works through the WebDriver BiDi protocol (the chromium-bidi package ships inside 25.14), but it is a second-class path, and WebKit does not exist.

Language coverage decides some team leads by itself. Playwright publishes first-party Python, Java, and .NET drivers, versioned in lockstep with the Node package (Python 1.64.0 sat beside npm 1.64.1 in the same release train). Puppeteer is Node-only, and the community Python port, pyppeteer, last shipped in February 2024 while Playwright’s Python driver matched the Node release this week. If your shop writes Python, this row of the table picks your tool.

The tooling loop is faster on Playwright, and the tools came over from its testing side. Codegen records a browser session and writes the script for you, and the trace viewer replays a failed run step by step with screenshots and network logs. When a selector dies because a site shipped a redesign, that replay cuts an afternoon down to minutes. The Playwright scraping tutorial leans on exactly this loop.

Where Puppeteer pulls ahead

PDF generation is Puppeteer territory. page.pdf() turns a page into a print-faithful document with headers, footers, and page ranges. Playwright has the method too, but only on Chromium: point the same script at its Firefox or WebKit engine and the call does not exist. If the job is “save 5,000 invoices as PDFs”, Puppeteer is the safer bet.

Raw Chrome access is the other one. Puppeteer speaks the Chrome DevTools Protocol directly, so when you need something exotic (CDP domains Playwright wraps or hides, Chrome extension hooks, the newest headless flags on the day Chrome ships them) you are one layer from the metal. The Chrome DevTools team maintains Puppeteer, which also means new Chrome features get bindings fast. The Puppeteer scraping tutorial covers that end of the API.

And the API is smaller. Fewer concepts, fewer locator flavors, one engine to reason about. For a fifty-line scrape-and-quit script, Puppeteer’s surface area is a feature.

The anti-bot reality, for both

Neither tool defeats Cloudflare or any modern bot manager, and marketing that says otherwise is selling you a proxy plan. Both drive automation-flagged browsers; both are detectable by TLS, header, and behavior fingerprinting before your first selector runs. The stealth ecosystems tell the story honestly: puppeteer-extra and its stealth plugin (both last published March 2023) patch known leaks but move slowly against current detectors, while Playwright’s npm stealth story is a 0.0.1 package from 2021. In practice people run either framework through rotating proxies and a real (non-headless-shell) Chrome channel and accept partial success. If a target’s robots.txt or terms forbid the scrape, the right tool is none of them.

One structural difference matters here: Puppeteer downloads a full Chrome and a headless shell, so switching to a real headed Chrome is a config value. Playwright’s headless shell is the cheap path, and headed means the full browser build.

When the browser is the wrong layer

A head-to-head between browser drivers assumes you need a browser at all. Most pages on the open web serve their content as HTML, and driving Chromium over them is fuel you burn for nothing. I scrape with Magpie for that tier, and the static-parser tools cover it too: fetch the HTML, parse it in-process, no browser bill. Magpie is a local CLI that fetches static HTML first, escalates to a real browser only when the page needs JavaScript, and respects robots.txt with per-host throttling. It does not bypass bot protection either, by design. For the pages that do need a driver, Puppeteer and Playwright are the two honest choices; this article exists so you pick once, correctly.

FAQ

Is Playwright better than Puppeteer for web scraping?

For most people starting today, yes: auto-wait removes the most common failure class, and Firefox or WebKit coverage handles sites that serve per-engine markup. Puppeteer still wins Chrome-only Node pipelines, PDF jobs, and raw CDP work.

Can Puppeteer control Firefox?

Partly. Puppeteer drives Firefox over the WebDriver BiDi protocol, and the chromium-bidi package ships in the main install, but Chromium is the first-class engine and WebKit is unsupported. If engine parity matters, Playwright is the tool.

Is Playwright faster than Puppeteer?

Not in any way that survives contact with a real workload. My two-run sample on one page: 2.4 to 2.7 seconds for Playwright, 3.0 to 3.2 for Puppeteer. Network time dominates real jobs. Pick on API, engines, and language.

Is Puppeteer deprecated?

No. Puppeteer 25.14.0 shipped October 14, 2026, and its repo took commits the day I checked. Google’s Chrome DevTools team maintains it and uses it in Chrome’s own tooling.

Do I still need Selenium?

For legacy grids and Internet Explorer-era coverage, sometimes. For new scraping work, no: Playwright covers what Selenium did with faster setup and better failures. The details live in the Selenium vs Playwright comparison.

The ten-minute tiebreaker

Install both, point the twelve-line script above at your actual target site, and run it twice. If Puppeteer’s selectors hold and you never leave Node, you are done in Chrome-land. The moment your target hydrates late, serves WebKit-different markup, or your team writes Python, Playwright keeps the win. Both cost zero in license fees; the real bill is the 1.3 GB of browsers and the weekend you do not lose to a race condition.

Stop renting credits.

One flat price, your machine, your keys.

Download free for Windows, macOS and Linux

Windows · macOS · Linux $49/mo flat, cancel anytime