Choosing Between Playwright, Selenium, and Puppeteer
A working framework for picking the right browser automation tool for your project, and when the answer is 'don't use any of them.'
What you’ll learn
- Compare the three tools across the four dimensions that actually matter: surface area, async model, ecosystem, anti-bot.
- Apply a fast decision tree to pick a tool for any new project.
- Recognise that the wrapper-language matters as much as the tool.
- Avoid the most common mis-choices: 'we always use Selenium' and 'Playwright for everything.'
Three tools, all capable, all with different shapes. The wrong choice costs you in lines of code, runtime cost, and hours debugging the wrong layer of the stack. This lesson is the framework for picking right.
The honest comparison
| Dimension | Playwright | Selenium | Puppeteer |
|---|---|---|---|
| Cross-browser | Chromium, Firefox, WebKit | Chromium, Firefox, Safari, Edge | Chromium only |
| Languages | Python, Node, Java.NET, PHP (via Panther wrapper) | Python, Node, Java, Ruby, C#, PHP | Node, Python (community port) |
| Auto-waiting | Built-in everywhere | None, explicit waits required | None (waitForX helpers, but no locator auto-wait) |
| Network interception | First-class route() API |
Via BiDi (newer) or Browser-Mob proxy | First-class via CDP |
| Locator stability | Lazy, re-queries each action | Stale on re-render | Same stale-element risk as Selenium |
| Multi-context per browser | Yes | One context per WebDriver session | Incognito contexts (clunkier) |
| Codegen | Yes, playwright codegen |
Selenium IDE (browser extension) | None official |
| Trace viewer | Yes, playwright show-trace |
None | None |
| Anti-bot ecosystem | playwright-stealth, rebrowser | undetected-chromedriver (Python), patched grids | puppeteer-extra-plugin-stealth |
| Maintained by | Microsoft | Open community | |
| CDP access | Available but wrapped | Not native (Selenium 4 BiDi is partial) | First-class, the entire library is CDP |
You can solve any scraping problem with any of these tools. The question is how much friction you'll eat.
The decision tree
Start here.
│
▼
Does your team have an existing Selenium codebase?
├── YES ──► Stay with Selenium for that codebase. New scripts can be Playwright.
│
▼
Are you maintaining or extending a Puppeteer codebase?
├── YES ──► Stay with Puppeteer.
│
▼
Is the project pure Chromium AND you need deep CDP access (perf traces, network emulation, coverage)?
├── YES ──► Puppeteer.
│
▼
Do you need cross-browser including Safari (true WebKit)?
├── YES ──► Playwright. Only it has real Safari/WebKit.
│
▼
Default: Playwright.
For new greenfield projects, Playwright wins ~80% of the time. The other 20% is legacy, deep-CDP, or "the rest of the company already uses X."
The language axis matters more than you think
The tool is half the choice. The wrapper language is the other half. A scraper team standardised on PHP shouldn't switch to Node just to use Playwright, they should reach for Symfony Panther. Inverse for a Python-only data team: don't add JS tooling just for Playwright Node. Stay on Python Playwright.
| Stack | Tool of choice |
|---|---|
| Python data team | Python Playwright |
| Node/TypeScript team | Node Playwright |
| Symfony / Laravel team | Symfony Panther (Playwright via PHP) |
| Java QA team | Selenium (or Playwright Java) |
| .NET team | Playwright .NET |
| Ruby team | Selenium (Playwright Ruby is community-only) |
| Multi-language polyglot scrape | Playwright Node + CLI (npx playwright) |
Compatibility with your existing pipeline matters more than tool-level micro-features.
The "we always use X" trap
Standardisation has real value (one toolchain, one runbook, one set of CI containers) but blind standardisation costs. Two anti-patterns:
"We always use Selenium because we always have." Selenium is fine, but new scrapers written in Selenium today carry the WebDriver overhead, the stale-element bugs, and the explicit-wait noise, pain Playwright eliminates. If the team is open to it, run a small Playwright pilot and measure code volume + flake rate vs the equivalent Selenium scraper.
"We use Playwright for everything, even tiny static scrapes." Browser automation is 10-50x slower than HTTP. If your target is server-rendered, HTTP scraping (Sub-Path 1) is dramatically cheaper. Playwright is fantastic at what it does, use it when JS rendering is the bottleneck, not for every scrape on principle.
Specific mis-choice patterns to avoid
- Using Selenium because "Playwright doesn't support cross-browser." It does. Playwright was built for cross-browser.
- Using Puppeteer because "Google made it." Microsoft made Playwright after hiring the Puppeteer team. Puppeteer is fine but Chromium-only.
- Using Playwright because "Selenium is slow." They both drive real browsers. Per-action latency is similar; Playwright wins on developer-time-per-script, not on browser execution speed.
- Using Selenium for new anti-bot work because of
undetected-chromedriver. Playwright has its own stealth ecosystem now (rebrowser-playwright,playwright-extra+ stealth plugin). Lesson 2.28.
Mixing tools is fine
A real production scraping platform sometimes uses several:
- Playwright Python for the main pipeline.
- Selenium Grid for cross-browser smoke tests once a day.
- Puppeteer with CDP for performance audits of competitor sites.
- Symfony Panther for the PHP-based admin tools that show scrape results.
Standardise where you can, mix where you must. Coherence is the goal, not monoculture.
Sub-Path 2 voice: Playwright as default
This sub-path is built on Playwright (Python, Node, and PHP via Panther) because it minimises the friction for the most readers. If you finish this curriculum and start a job that uses Selenium or Puppeteer, you have the conceptual model, the syntactic translation is straightforward. The hard parts are waiting strategies, locator design, and detection avoidance, all of which carry across.
Hands-on lab
This lesson has no lab. Instead: pick two scraping projects you've worked on (or three from your immediate roadmap). For each, walk through the decision tree above and write down which tool you'd pick today, and which you actually used. Note the gap. That gap is what this lesson is for.
Quiz, check your understanding
Pass mark is 70%. Pick the best answer; you’ll see the explanation right after.