Playwright vs Puppeteer vs Selenium for SERP Scraping in 2026

By Serpent API Team · · 12 min read

Pick a browser automation framework and you have implicitly picked a stealth strategy, a speed ceiling, and a maintenance burden for the next year. For search-results scraping — where the target actively fights you — that choice matters more than almost any other line in your scraper.

The three contenders are Puppeteer, Playwright, and Selenium. They get pitched as wildly different, but for the part that decides whether you get blocked, they are far more alike than the marketing suggests. The real differences are in language reach, the stealth tooling around each, and the small speed and memory deltas that compound at scale.

This guide runs the same Google search scrape in all three so you can compare them line for line, explains why they all leak the same way, surveys the stealth ecosystem for each, and ends with a recommendation by use-case.

TL;DR: All three drive Chromium over the same DevTools Protocol, so out of the box they are detected the same way — the framework is not your stealth strategy, the patched runtime is. Puppeteer (Node only) and Playwright (multi-language, multi-browser) are nearly identical in speed and have the richest stealth options; Selenium has the broadest language and grid support but is best paired with undetected-chromedriver or nodriver. For greenfield SERP work, start with Playwright or a CDP-direct stealth runtime. For zero detection headaches, a SERP API returns JSON and you write no browser code at all.

The three tools at a glance

Before the nuance, the headline differences. The table below is the thirty-second version — the GitHub star counts are approximate and move constantly, so treat them as rough popularity signals rather than exact figures.

 PuppeteerPlaywrightSelenium
Language supportJavaScript / TypeScript (official); Python via a community portJS/TS, Python, Java, .NET (all official)Python, Java, JS, C#, Ruby, and more
GitHub stars (approx.)~90k~70k~32k (core)
Multi-browserChromium & FirefoxChromium, Firefox & WebKitEvery major browser via WebDriver
Network interceptionYes — setRequestInterceptionYes — page.route()Limited (better via the bidirectional protocol)
Stealth ecosystemMature — puppeteer-extra & patched runtimesStrong — patched Chromium buildsGood — undetected-chromedriver, nodriver

The pattern is already visible. Selenium wins on reach — languages, browsers, grid tooling. Playwright matches it on languages while keeping the modern, fast architecture. Puppeteer is the narrowest (Node-first) but has the deepest stealth lineage. For a broader build-versus-buy framing, see web scraping versus a SERP API.

Detection parity: they all leak the same way via CDP

Here is the part that surprises people: out of the box, none of these three is meaningfully harder to detect than the others. They share the same fundamental weakness.

Puppeteer and Playwright both control Chromium directly over the Chrome DevTools Protocol (CDP). Modern Selenium, when driving Chrome, also speaks CDP internally for many operations. That shared protocol is the leak: certain CDP activity is observable from inside the page, and an anti-bot script can watch for it. The most discussed example is the way the protocol's runtime instrumentation can be probed from page JavaScript to infer that a debugger-style controller is attached.

On top of the protocol itself, all three launch a browser that, by default, carries the classic automation tells — the navigator.webdriver flag, headless-specific quirks in fonts and codecs, and WebGL or user-agent values that do not match a real install. Those signals do not care which framework set them. The full catalogue is in how to beat headless Chrome detection.

The practical conclusion: the framework name is not your anti-detection strategy. What changes your fingerprint is the patched runtime you layer on top — the build that closes the CDP and property leaks. That is why two scrapers using different frameworks but the same patched Chromium look nearly identical to a defender, and why the choice of framework should be driven by ergonomics and ecosystem rather than a belief that one is inherently "stealthier." We dig into one specific leak and what still works in does puppeteer-extra-plugin-stealth still work in 2026.

Stealth options per tool

Since the runtime is what matters, the right question is: which patched runtime does each framework have, and how actively is it maintained? Maintenance is everything here, because anti-bot vendors patch their detections continuously, and an abandoned stealth library is worse than none — it gives false confidence.

FrameworkPrimary stealth option(s)Approach
Puppeteerpuppeteer-extra-plugin-stealth; patchrightJS-property patches in-page; or a patched Node runtime that closes the CDP leak
Playwrightpatchright (patched Playwright runtime)A drop-in patched build that hides the same protocol-level tells
Seleniumundetected-chromedriver; nodriverPatched ChromeDriver, or a CDP-direct successor that drops WebDriver entirely

puppeteer-extra-plugin-stealth is the veteran. It applies a stack of evasions to the page — spoofing navigator.webdriver, fixing the headless codec list, normalising the permissions API, and more. It still closes a lot of the easy property-based checks, but the newer protocol-level detections it cannot reach, which is exactly the gap the patched-runtime projects target.

patchright is a notable newer entrant: a patched, drop-in replacement for Playwright (and a sibling for Puppeteer) that closes the runtime-instrumentation leak at the source rather than papering over it in page script. You can read the project's own description of what it patches on its GitHub repository.

undetected-chromedriver has long been the Selenium answer — a ChromeDriver build that strips the obvious automation markers. The momentum in the Python world, though, has shifted toward nodriver, its async successor, which talks to the browser directly over CDP and skips the WebDriver layer altogether, sidestepping a whole class of driver-level tells. All of these projects move fast; check recent commit activity before you commit your stack to one.

The same scrape in all three

The cleanest way to compare the developer experience is to write the identical task — search Google, read the organic result titles and URLs — in each. The shape is the same everywhere: set a consent cookie, navigate, then read the DOM. Only the API surface changes.

First, Puppeteer in Node, with the stealth plugin applied:

// npm install puppeteer-extra puppeteer-extra-plugin-stealth
const puppeteer = require('puppeteer-extra');
const Stealth = require('puppeteer-extra-plugin-stealth');
puppeteer.use(Stealth());

(async () => {
  const browser = await puppeteer.launch({
    headless: 'new',
    args: ['--no-sandbox', '--disable-blink-features=AutomationControlled'],
  });
  const page = await browser.newPage();
  await page.setCookie({ name: 'CONSENT', value: 'YES+', domain: '.google.com' });

  const q = 'best running shoes 2026';
  await page.goto(
    `https://www.google.com/search?q=${encodeURIComponent(q)}&hl=en&gl=us`,
    { waitUntil: 'domcontentloaded', timeout: 30000 }
  );

  const results = await page.$$eval('a:has(h3)', (links) =>
    links.map((a) => ({
      title: a.querySelector('h3').innerText,
      url: a.href,
    }))
  );

  console.log(results.slice(0, 10));
  await browser.close();
})();

Now the same thing in Playwright (Python here, to show the multi-language reach). Note how close the logic is — add_cookies, goto, then a DOM query:

# pip install playwright  &&  playwright install chromium
from playwright.sync_api import sync_playwright
from urllib.parse import quote

with sync_playwright() as p:
    browser = p.chromium.launch(
        headless=True,
        args=["--no-sandbox", "--disable-blink-features=AutomationControlled"],
    )
    context = browser.new_context()
    context.add_cookies([
        {"name": "CONSENT", "value": "YES+", "domain": ".google.com", "path": "/"}
    ])
    page = context.new_page()

    q = "best running shoes 2026"
    page.goto(
        f"https://www.google.com/search?q={quote(q)}&hl=en&gl=us",
        wait_until="domcontentloaded", timeout=30000,
    )

    results = page.eval_on_selector_all(
        "a:has(h3)",
        "els => els.map(a => ({title: a.querySelector('h3').innerText, url: a.href}))",
    )
    print(results[:10])
    browser.close()

And finally Selenium in Python. The classic WebDriver flow is wordier — you start a driver, then locate elements — though the end result is the same list of titles and URLs:

# pip install selenium
from urllib.parse import quote
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By

opts = Options()
opts.add_argument("--headless=new")
opts.add_argument("--no-sandbox")
opts.add_argument("--disable-blink-features=AutomationControlled")
driver = webdriver.Chrome(options=opts)

driver.get("https://www.google.com")
driver.add_cookie({"name": "CONSENT", "value": "YES+", "domain": ".google.com"})

q = "best running shoes 2026"
driver.get(f"https://www.google.com/search?q={quote(q)}&hl=en&gl=us")

results = []
for a in driver.find_elements(By.CSS_SELECTOR, "a:has(h3)"):
    results.append({
        "title": a.find_element(By.CSS_SELECTOR, "h3").text,
        "url": a.get_attribute("href"),
    })

print(results[:10])
driver.quit()

Three frameworks, one task, almost identical logic. The selector a:has(h3) is deliberately simple for the comparison; in production Google rotates its class names constantly, so you want a resilient multi-selector chain — that is the whole subject of building a resilient scraper that survives selector drift. If you would rather skip the browser entirely, the pure-HTTP route is covered in scraping Google in Node without Puppeteer.

Speed & memory for SERP work

The architecture explains the performance differences. Puppeteer and Playwright connect to the browser directly over a WebSocket using the DevTools Protocol — there is no separate process between your script and Chromium. Each command is a single round trip to the browser.

Classic Selenium inserts a driver process (ChromeDriver) between your code and the browser, translating WebDriver commands into browser actions. That extra hop costs a few milliseconds per command, which is invisible for a click but adds up across thousands of element lookups. Modern Selenium can use the W3C BiDi (bidirectional) protocol to talk more directly and narrow much of this gap, but the legacy path still carries the overhead.

DimensionPuppeteerPlaywrightSelenium
Browser transportCDP over WebSocket (direct)CDP over WebSocket (direct)WebDriver via driver; BiDi optional
Per-command latencyLowLowSlightly higher on the classic path
Memory per browserChromium baselineChromium baselineChromium + driver process
Parallelism modelPages / contextsBrowser contexts (cheap isolation)Sessions, often via a grid

Two caveats keep this honest. First, the differences are small — for one search request, all three feel instant. Second, and more importantly, the dominant cost in SERP scraping is not the framework but the network: proxy round-trip time and the bytes you pull down. A page laden with imagery over a residential proxy dwarfs any per-command delta, which is why blocking heavy resources — the technique in slashing scraping bandwidth by blocking resources — buys you far more than switching frameworks. The lighter route, when you can avoid a browser, is the pure-HTTP path in scraping Google for free.

Where Playwright pulls slightly ahead operationally is its browser-context model: spinning up an isolated context (its own cookies, cache, and storage) is cheap, so you can run many independent sessions inside one browser process without the memory cost of one process each. For high-throughput SERP fleets that is a real efficiency win, and it pairs well with the patterns in the wider scraping benchmarks at Apify's Playwright versus Puppeteer comparison.

A recommendation by use-case

There is no single winner — the right tool depends on what constraints you are optimising for. Map your situation to one of these and the choice is usually obvious.

Your situationBest starting pointWhy
Node-only team, deep stealth needsPuppeteer + patched runtimeMature stealth lineage; direct CDP control
Python or polyglot team, greenfieldPlaywrightOfficial multi-language API, cheap contexts, strong patches
Already invested in Selenium / QA gridSelenium + undetected-chromedriver or nodriverReuse existing skills and infra; add the patched runtime
Need WebKit / Safari renderingPlaywrightOnly one with first-class WebKit support
Want zero detection & maintenanceA managed SERP APINo browser, no proxies, no parser upkeep — just JSON

That last row is not a throwaway. Every framework in this comparison still leaves you owning the hard parts: a proxy budget, a stealth runtime you must keep patched, selectors that drift weekly, and the on-call burden of figuring out why the scraper went quiet — the failure modes in why your SERP scraper breaks at 3 a.m. The framework choice optimises the easy 20% of the job; the other 80% is operations you carry regardless.

The verdict

If you are starting fresh in 2026 and stealth plus language flexibility are the priorities, Playwright is the most balanced default: official multi-language support, the modern direct-CDP architecture, cheap browser contexts for parallelism, and a healthy patched-runtime option. Puppeteer remains an excellent choice for Node-first teams that value its deep stealth heritage and tight control.

Selenium is far from dead — its language reach, grid tooling, and community are unmatched, and paired with undetected-chromedriver or nodriver it scrapes search results perfectly well. Reach for it when you are already running it, or when you need a browser or language the others do not cover.

But the unifying lesson is the one that should reframe the whole debate: none of them is your stealth strategy. They all leak the same way through the same protocol, so your real defence is the patched runtime, the proxy quality, and the operational discipline around them. Choose the framework that fits your team and stack — then spend your energy on the parts that actually decide whether you stay unblocked.

Skip the framework debate. Just get the results.

Serpent's SERP API returns clean JSON from Google, Bing, Yahoo & DuckDuckGo — no proxies, no CAPTCHAs, no parser maintenance. Get 10 free searches on signup, then pay-as-you-go from $0.03 per 10,000 searches at scale, no subscription.

Get Your Free API Key

Explore: SERP API · Pricing · Playground

FAQ

Which is least detectable: Playwright, Puppeteer, or Selenium?

None of them is meaningfully less detectable out of the box, because all three drive Chromium over the same Chrome DevTools Protocol, which leaks the same low-level signals. What actually changes your detectability is the patched runtime you layer on top: a stealth build that closes the known CDP and JavaScript-property leaks. Picking the framework with the most actively maintained patch is more important than the framework itself.

Which is fastest for SERP scraping?

Puppeteer and Playwright are close, because both talk to the browser directly over a WebSocket using the DevTools Protocol with no intermediate driver process. Selenium's classic WebDriver path adds a separate driver hop that costs a little latency per command, though modern Selenium can use the bidirectional protocol to narrow the gap. For high-throughput SERP work the practical bottleneck is usually network and proxy latency, not the framework, so blocking heavy resources matters far more than the tool you choose.

Which has the best stealth ecosystem?

Puppeteer and Playwright have the richest options. Puppeteer has the long-standing puppeteer-extra-plugin-stealth and newer patched runtimes; Playwright is well served by patched Chromium builds that close the same leaks. Selenium's strongest options are undetected-chromedriver and the newer nodriver project, which take the CDP-direct approach rather than the classic WebDriver path. The ecosystems shift quickly, so check the project's recent commit activity before committing.

Is Selenium still worth using in 2026?

Yes, in the right situations. Selenium has the broadest language support, the largest community, and unmatched cross-browser and grid tooling, which makes it a sensible default for teams that already run it for QA. For greenfield SERP scraping where stealth and speed are the priorities, Playwright or a CDP-direct stealth runtime is usually a better starting point, but Selenium remains a perfectly capable choice when paired with undetected-chromedriver or nodriver.