Skip to main content

Playwright vs Puppeteer vs Selenium 2026: 3 Browsers, 1 Winner

· 8 min read
Yassine El Haddad
Software Developer & Automation Specialist

I build production AI agents, web scrapers, and automation pipelines. Most of what I publish here comes from the actual problems they run into: proxies that get banned, anti-bot stacks that fingerprint your client, RAG that drifts when the underlying data moves. Stack: Python, TypeScript, Go, FastAPI, LangChain, Crawlee, Playwright, deployed on AWS, GCP, and Cloudflare.

Quick Answer

Default to Playwright for new browser automation: one install drives Chromium, Firefox, and WebKit with built-in auto-wait, ~50–80 MB per context, and Crawlee/Apify integration. Pick Puppeteer 25 for Chrome-only Node services that benefit from raw CDP or the new WebDriver BiDi transport. Keep Selenium 4 when you need Java/C#, Selenium Grid, or BiDi network logging across legacy suites.

In 2026, three tools dominate browser automation: Playwright (Microsoft), Puppeteer 25 (Google, now with WebDriver BiDi), and Selenium 4 (cross-vendor, BiDi-capable). The right choice depends on your use case: new projects should default to Playwright; Puppeteer fits Chrome-focused, lightweight needs; Selenium remains for Java/C# teams and legacy systems. Run Playwright on Apify.

TL;DR: Winner by Use Case

Use CaseWinnerWhy
Web scraping, new projectsPlaywrightAuto-wait, multi-browser, Crawlee/Apify integration
Chrome-only, raw CDP controlPuppeteerLighter, direct CDP access, Google ecosystem
Java/C#/legacy enterpriseSeleniumMulti-language, WebDriver standard, large install base
Anti-bot bypass, production scrapersPlaywright + ApifyProxy rotation, Crawlee, Apify infrastructure
High-volume, simple HTMLNeither — use HTTP + proxyCheaper, faster than browser rendering

Benchmark snapshot (numeric comparison)

Use these as order-of-magnitude anchors—not a substitute for profiling your pages, proxies, and hardware. Figures assume headless Chromium, local runs, and sequential navigation to lightweight static URLs (not SPAs behind anti-bot).

MetricPlaywrightPuppeteerSelenium (ChromeDriver)
Cold start to first real navigation¹~0.4–0.7 s~0.3–0.5 s~1.2–2.5 s
Idle process RAM after launch²~90–130 MB~60–100 MB~180–280 MB
Simple static pages / minute³~35–55~40–60~18–35

¹ Wall-clock from fresh Node process to page.goto() settling on a small HTML document; OS and browser channel dominate variance.

² RSS-style footprint for one browser instance; Docker and concurrent contexts multiply this.

³ Sequential goto to fast static hosts; SPAs, screenshots, or network throttling cut throughput sharply.

Why Selenium trails on throughput: WebDriver’s command round-trips and lack of native auto-wait usually mean more sleeps, retries, and driver chatter per step than CDP-first clients.

Playwright Overview

Playwright is Microsoft's browser automation framework. It supports Chromium, Firefox, and WebKit from a single API. Key features:

  • Auto-waiting — Actions wait for elements to be actionable (visible, stable, not obscured)
  • Isolated contexts — Each context gets its own cookies, storage, and proxy
  • Built-in test runner — Parallel runs, screenshots, traces, codegen
  • Codegennpx playwright codegen records interactions into scripts

Playwright is the default choice for scraping and testing in 2026. It scales well with Crawlee (maintained by Apify) and runs on Apify Actors for production deployments.

Puppeteer Overview

Puppeteer (currently v25) is Google's Chrome automation library. It connects via the Chrome DevTools Protocol (CDP) and now also speaks WebDriver BiDi, the W3C BiDirectional protocol:

  • Chrome and Firefox — Node.js only; Python via pyppeteer (community)
  • Raw CDP accesspage._client for low-level control
  • WebDriver BiDi transport — opt-in standard-compliant protocol for Firefox/Chromium
  • Lighter weight — Smaller binary, headless by default in v25
  • No built-in test runner — Use Jest, Mocha, or custom scripts

Puppeteer suits teams that need tight CDP control or already run Chrome-only pipelines. For multi-browser and scraping at scale, Playwright is usually better.

Selenium Overview

Selenium 4 uses the W3C WebDriver protocol plus the newer WebDriver BiDi standard. A driver binary (or Selenium Manager) sits between your code and the browser:

  • Multi-language — Python, Java, C#, Ruby, JavaScript, Kotlin
  • Selenium Manager — Rust-based tool auto-fetches drivers and browsers in v4
  • WebDriver BiDi — Bidirectional events for logging, network, script (no longer polling-only)
  • Manual waiting — No auto-wait; requires WebDriverWait and explicit conditions

Selenium is best when you're locked into Java/C# or have large existing test suites. For greenfield scraping or testing, Playwright reduces flakiness and maintenance. The WebDriver protocol also integrates with enterprise test grids (Sauce Labs, BrowserStack) that some teams already use for cross-browser validation.

Feature Comparison

FeaturePlaywrightPuppeteerSelenium
BrowsersChromium, Firefox, WebKitChrome, FirefoxAll (via drivers)
LanguagesNode, Python, .NETNode (Python via pyppeteer)Python, Java, C#, JS, etc.
Auto-wait✓ Built-inPartial✗ Manual only
Network interception✓ route, fulfill✓ request interceptionLimited
Parallel contexts✓ Native, isolatedModerateHeavier (new process)
Test runner✓ Built-in✗ Use Jest/Mocha✗ Use TestNG, etc.
Codegen✓ Built-in
CDP accessVia page.context().newCDPSession()✓ Native

Playwright leads on developer experience and scraping reliability. Puppeteer excels for CDP-level control. Selenium remains for language and toolchain compatibility.

Performance Comparison

MetricPlaywrightPuppeteerSelenium
Startup time~1–2s~1–2s~2–4s (driver + browser)
Memory per context~50–80 MB~60–90 MB~80–120 MB
CI build timeFast (pre-install)FastSlower (driver matrix)

For high-volume scraping, startup and memory multiply. Playwright's context isolation lets you deploy many lightweight contexts; Selenium often requires new processes. Use Apify to run Playwright at scale without managing infrastructure.

Web Scraping: Playwright vs Puppeteer

Both integrate with Apify and Crawlee. Playwright is preferred for scraping because:

  • Crawlee is built for Playwright first; Puppeteer support exists but is secondary
  • Auto-wait eliminates most flakiness from dynamic content
  • Multi-browser helps when sites behave differently per browser
  • Apify Actors default to Playwright for new scrapers

Puppeteer is acceptable for Chrome-only, low-complexity targets. For anti-bot sites (LinkedIn, Cloudflare, Amazon), pair Playwright with Bright Data Scraping Browser or Apify's proxy configuration for residential IPs.

Debugging and Tracing

Playwright includes built-in tracing: context.tracing.start() before a run, stop() after, and trace.playwright.dev to replay. You see exactly which selectors were used, network requests, and DOM snapshots. This dramatically reduces debugging time for flaky tests or extraction failures. Puppeteer supports similar DevTools traces. Selenium lacks native tracing; you rely on screenshots and logs. For production scrapers on Apify, enable tracing on failed runs to diagnose issues without reproducing locally.

Migration: Selenium to Playwright

When to migrate:

  • Frequent StaleElementReferenceException or timing issues
  • Need for parallel, isolated contexts
  • Desire for built-in codegen and tracing
  • Moving to Apify or Crawlee for deployment

Steps:

  1. Installnpm init playwright@latest or pip install playwright && playwright install
  2. Port selectors — Replace XPath/CSS; Playwright prefers getByRole, getByText
  3. Remove explicit waits — Delete time.sleep and WebDriverWait where Playwright auto-waits
  4. Run in parallel — Use browser.newContext() per worker instead of new processes

See building an Apify Actor with TypeScript for deployment patterns.

Comparison Table: Full Stack

AttributePlaywrightPuppeteerSelenium
EcosystemCrawlee, Apify, AzureChrome team, communitySelenium Grid, vendors
Stealth / anti-botCrawlee stealth, pluginsundetected-chromedriver styleundetected-chromedriver
DeploymentApify, Docker, CISameSelenium Grid, cloud providers
Learning curveModerateLow (Chrome devs)Moderate–high
2026 recommendation✓ Default for new workChrome-only needsLegacy, Java/C#

Bottom line: For web scraping and modern test automation, choose Playwright. Use Puppeteer when you need raw CDP or are Chrome-only. Keep Selenium only when language or legacy constraints require it. For production scrapers, deploy Playwright via Apify and handle errors with robust patterns.

Apify Affiliate Banner 728x90Apify Affiliate Banner 728x90Apify Affiliate Banner 300x50Apify Affiliate Banner 300x50
Playwright + Apify

Build once with Playwright locally, then deploy to Apify for scaling, scheduling, and integrations. No server management, built-in proxy and storage.



Try Apify | Build an Actor

Frequently Asked Questions

Playwright. It has auto-wait, multi-browser support, and tight Crawlee/Apify integration. Puppeteer is fine for Chrome-only, lightweight scripts.

When you're on Java/C#, have large legacy suites, or need WebDriver compatibility with cloud grids (Sauce Labs, BrowserStack) that don't support Playwright yet.

Yes. Apify Actors use Playwright (and Puppeteer). Crawlee, maintained by Apify, provides the scraping abstraction on top of Playwright.

Replace WebDriver with Playwright's browser/context/page API. Remove explicit waits; use Playwright's auto-wait. Prefer getByRole and getByText over raw XPath.

Startup and runtime are similar. Playwright's context model can be more memory-efficient for many parallel sessions. Selenium is typically slowest due to HTTP polling.

Playwright alone doesn't bypass anti-bot. Use Crawlee's stealth plugins, Apify proxies, or Bright Data Scraping Browser for protected sites.

Common mistakes and fixes

Playwright tests flaky or timing out

Use auto-wait; avoid page.waitForTimeout. Prefer waitForSelector or waitForLoadState. Check for race conditions.

Selenium StaleElementReferenceException

Re-query elements before use. Use explicit WebDriverWait. Consider migrating to Playwright for native auto-wait.