Playwright vs Selenium: Browser Automation for Scraping Compared (2026)
Some links on this page are affiliate links. We earn a commission if you sign up – at no additional cost to you. Our editorial assessment is independent and never paid. How we review.
| Attribute | Playwright | Selenium |
|---|---|---|
| Pricing tier | Free | Free |
| Free tier | Yes | Yes |
| JS rendering | Yes | Yes |
| Structured output | No | No |
| Open source | Yes | Yes |
| Self-host | Yes | Yes |
| Primary category | Crawling Frameworks | Crawling Frameworks |
| Notable strength | A solid choice when a target needs full JavaScript rendering: it's backed by Microsoft and covers three browser engines behind one API. | Selenium's WebDriver bindings across six languages and its Grid for distributed runs suit teams already doing cross-browser testing who… |
Selenium turned twenty-two this year and still shipped 4.46.0 on 11 July 2026. Playwright's release notes were on the 1.62 line the same month. Neither project is fading, which is why the fork every crawler hits when plain HTTP stops working is still a real decision. Requests plus a parser fails, either because the data arrives via XHR after paint or because the target starts scoring behaviour rather than headers, and someone proposes driving a real browser. The team picks Playwright or Selenium, and the choice tends to stick for years, because it decides the shape of the worker, the retry logic, the proxy plumbing and the memory budget.
Both live in the same crawling frameworks category and both were built for testing rather than data collection. That shared origin is why most of the comparisons you will find weigh them as test runners. Scraping asks different questions. A test that flakes goes red and someone looks at it. A scraper that flakes writes a null into a database and nobody looks at it for a month. That asymmetry is the reason waiting behaviour matters more here than it does in CI.
Head to head
| Dimension | Playwright | Selenium |
|---|---|---|
| Origin | Microsoft, single API across engines | First released 2004, governed by the Software Freedom Conservancy |
| Current release | 1.62 is the newest line in the release notes (read 2026-07-27) | 4.46.0, dated 11 July 2026 on selenium.dev/downloads (read 2026-07-27) |
| Languages | Node.js, Python, Java, .NET | .NET/C#, Ruby, Java, Python, JavaScript as the core bindings, plus third-party ports |
| Browsers | "Chromium, WebKit and Firefox on Windows, Linux and macOS" | Anything with a W3C WebDriver implementation, including Edge, Safari and older builds |
| Waiting | Auto-waits on actionability checks before every action | Implicit, explicit and fluent waits, written by you |
| Isolation unit | Browser context inside one browser process | WebDriver session, usually one browser process |
| Per-session proxy | Yes, proxy on newContext() with credentials | Per session via the Proxy object on driver options |
| Distributed scaling | No first-party fleet manager | Grid, in Standalone, Hub and Node, or Distributed mode |
| Protocol | Its own driver protocol per engine | W3C WebDriver Classic, migrating to WebDriver BiDi |
| Detection default | Announces automation in a default launch | navigator.webdriver is true by specification |
| Licence cost | Free, open source | Free, open source |
Waiting is the whole argument
Selenium gives you three wait strategies and expects you to choose. Implicit waits are a session-wide setting applied to every element lookup. Explicit waits poll for a named condition. FluentWait is the customisation layer for polling interval, ignored exceptions and timeout message. The documentation is blunt about combining the first two: "Do not mix implicit and explicit waits. Doing so can cause unpredictable wait times." It gives the arithmetic, where a 10 second implicit wait alongside a 15 second explicit wait can produce a timeout at 20 seconds.
That warning is easy to respect in a 40-test suite and hard to respect in a crawler that has absorbed six years of per-domain special cases. Nothing crashes when you get it wrong. You get a find_element that resolves against a stale DOM and returns the loading placeholder instead of the price.
Playwright moves the waiting into the library. Before every action it runs actionability checks: visible, stable, receives events, enabled, and editable where relevant. The docs put it plainly: "It auto-waits for all the relevant checks to pass and only then performs the requested action." When the checks do not pass, "action fails with the TimeoutError". Assertions behave the same way, with auto-retry until the condition holds.
For scraping the important half of that is the failure mode, not the success. A TimeoutError is loud. It lands in your logs, increments a counter and can trigger a retry against a different exit IP. A silent read of a placeholder does none of those things. Playwright turns a class of quiet data corruption into a noisy exception, and that is worth more than any throughput number.
Selenium can reach the same place. Explicit waits with well-chosen conditions, wrapped in a helper every call site uses, get you most of the way. The difference is that Playwright's version is the default and Selenium's is a convention your team has to keep enforcing through staff turnover.
Languages and browsers
This is the section where Selenium's age pays off. The downloads page lists five core bindings maintained by the main project: .NET/C#, Ruby, Java, Python and JavaScript, all at 4.46.0 on 11 July 2026, and notes that "language bindings for other languages exist" outside that set. Playwright covers Node.js, Python, Java and .NET.
The practical gap is Ruby, and it is not a small one for teams whose data platform is a Rails monolith. If your scraping code has to live in the same repository as your application code and that application is Ruby, Selenium is the answer and the rest of this comparison is academic. The same logic applies to any shop with a hard language mandate that Playwright does not cover.
Browsers cut the other way at the edges. Playwright's own summary is "Chromium, WebKit and Firefox on Windows, Linux and macOS, locally or in CI, headless or headed, with native mobile emulation for Chrome (Android) and Mobile Safari". Three engines behind one API is more than most scraping work needs, since the overwhelming majority of targets get crawled with Chromium. Selenium's reach is wider in a different sense: it drives anything that implements W3C WebDriver, which includes Edge, Safari and browser builds Playwright never bundled. That matters for a compliance-driven crawl that has to prove it collected pages as a specific browser version, and almost never otherwise.
Note the version numbers above are what each project's own page showed on 2026-07-27. Both ship frequently, so check before you quote them.
Proxies per session
Scraping diverges hardest from testing here, and the architectural difference bites. Playwright takes a proxy at launch:
const browser = await chromium.launch({
proxy: { server: 'http://myproxy.com:3128', username: 'usr', password: 'pwd' }
});
and again per context:
const context = await browser.newContext({
proxy: { server: 'http://myproxy.com:3128' }
});
The API reference describes the context-level option as "Network proxy settings to use with this context. Defaults to none", with server, bypass, username and password. Credentials are first class, which means an authenticated proxy network works without a shim.
Combine that with what contexts are. Playwright's documentation calls them "fast and cheap to create and are completely isolated, even when running in a single browser", and describes them as "equivalent to incognito-like profiles". One Chromium process, twenty contexts, twenty exit IPs, twenty cookie jars. For anyone running a rotating proxy pool where identity has to be pinned per session, that maps onto the problem almost exactly.
Selenium configures a proxy through the Proxy object on the driver options before the session starts:
options.proxy = Proxy({'proxyType': ProxyType.MANUAL, 'httpProxy': 'http.proxy:1234'})
Per session, this works fine, and a Grid node can hand out sessions with different proxies. The friction is credentials. The options documentation I read on 2026-07-27 shows proxyType and httpProxy and does not document username and password fields, which is why the usual production patterns are IP allowlisting with the proxy vendor, or a local forwarding proxy that injects auth. Both are workable. Both are one more moving part in the deployment.
The deeper difference is granularity. Selenium's proxy attaches to the session, and a session normally costs you a browser process. Playwright's attaches to a context, and a context costs you far less. At ten workers nobody notices. At several hundred concurrent sessions it is the difference between a fleet that fits your instance sizing and one that does not. The economics of that are the subject of our note on headless Chrome fleet costs.
Scaling across machines
Selenium is straightforwardly ahead here, and Grid is the strongest argument for choosing it in 2026. Its stated purpose is that it "allows the execution of WebDriver scripts on remote machines by routing commands sent by the client to remote browser instances", with three goals: parallel runs across machines, testing on different browser versions, and cross-platform testing. It ships in three deployment shapes. Standalone "combines all Grid components seamlessly into one" and gives "a fully functional Grid with a single command, within a single process". Hub and Node splits the router from the workers so you can mix operating systems and browser versions. Distributed runs each of the six components separately: Event Bus, New Session Queue, Session Map, Distributor, Router and Nodes.
That is a real distributed system with a queue, a session registry and a router, maintained by the project, and plenty of infrastructure teams already run one for QA. Pointing a crawler at an existing Grid is a small piece of work.
Playwright has no first-party equivalent. You scale it by writing the orchestration yourself, usually a queue plus a pool of containers, or by renting the fleet from a browser infrastructure vendor such as Browserbase that exposes a remote endpoint you connect to. That is a genuine gap dressed up as flexibility. Which side of it you want depends on whether you would rather operate a Grid or pay someone per browser-hour, a trade we cover in self-hosted vs managed browsers.
Worth being precise about the caveat: a Grid built for cross-browser QA is tuned for a few hundred short sessions a day, not for sustained crawling with per-session proxies and hostile targets. Reusing it is a head start, not a finished crawl fleet. The wider design question is in our guide to large-scale crawl architecture.
Detection surface
Neither tool is stealthy, and grading them on a curve would be dishonest. The W3C WebDriver specification defines a webdriver-active flag that "is set to true when the user agent is under remote control. It is initially false", exposed to pages through NavigatorAutomationInformation as navigator.webdriver. The spec is explicit about intent: it provides "a standard way for co-operating user agents to inform the document that it is controlled by WebDriver, for example so that alternate code paths can be triggered during automation". Selenium is standards-compliant, so it sets the flag. Playwright's default Chromium launch reports it too. Either way, a default launch tells the page it is automated before you have parsed a single element.
The flag is the trivial part. Any serious anti-bot system fingerprints rendering, fonts, audio, TLS handshake, header order and mouse behaviour, and correlates all of it with the IP reputation of your proxy. Patching one boolean does nothing against that. The reasons crawlers get caught are catalogued in why scrapers get blocked.
The practical consequence is that this section should not decide your choice. Teams working hard targets either bolt a fingerprint-management layer onto whichever framework they picked, move to a dedicated antidetect browser, or stop driving browsers themselves. Neither Playwright nor Selenium advertises evasion, and both are correct not to.
One forward-looking difference is protocol direction. Selenium is "updating its entire implementation from WebDriver Classic to WebDriver BiDi (while maintaining backwards compatibility as much as possible)", a cross-browser bidirectional standard developed with browser vendors, covering logging, network and script features. If BiDi lands broadly, Selenium gets network interception and event streams through a standard rather than through Chrome DevTools Protocol. That is a strategic reason to keep Selenium in view even if you would not choose it today, though the docs describe the work as ongoing rather than finished, so treat it as direction rather than a shipped capability.
What each one is bad at
Playwright's weaknesses are operational. There is no Grid. You will build or buy the fleet layer. The Ruby gap is real. It also has no crawling machinery of its own, so queueing, deduplication, retries and session rotation are yours to write, which is exactly the void that Crawlee fills by wrapping it. And it moves fast. Frequent releases with meaningful behaviour changes are a benefit when you are actively developing and a maintenance tax when a crawler has been running untouched for eighteen months.
Selenium's weaknesses are ergonomic and they compound. The API is verbose for extraction work, since it was designed around asserting on an application rather than harvesting from one. Waiting is your responsibility and the documentation warns you about the most tempting way to get it wrong. Proxy authentication needs a workaround. The session-per-browser model costs more memory per unit of isolation. None of these blocks a project. Together they mean more crawler code, and crawler code is where the silent bugs live.
Both share the same limitation: they are automation libraries rather than unblocking products. Neither ships a proxy pool, a CAPTCHA path or a fingerprint profile. That layer is a separate purchase, which is the point argued in crawling frameworks and the blocking layer.
Who should not pick either
Do not pick Playwright if your team writes Ruby and cannot introduce a second language, if you already operate a healthy Grid and your targets are not especially defended, or if you need a browser build Playwright does not bundle.
Do not pick Selenium for greenfield scraping. If nobody on the team is already running WebDriver in production, adopting it now means writing waiting logic that Playwright would give you for free, and paying a process per isolated session for the life of the crawler.
And do not pick either if your real problem is blocking rather than rendering. A team spending most of its week on evasion is buying the wrong layer: a managed scraping API absorbs that work, and an agent-driven wrapper like Stagehand is a different answer again for interaction-heavy targets. Our guide to choosing web scraping tools covers where each layer belongs, and if the choice you are actually making is between two crawling libraries rather than two browser drivers, Scrapy vs Crawlee is the closer comparison.
For most new scraping work in 2026 the answer is Playwright, chosen for auto-waiting and cheap contexts rather than for speed. Selenium is the right answer when your constraints are organisational: the language you must use, the browsers you must cover, the Grid you already run. Those constraints are common enough to keep a 22-year-old project in active use, which is mostly what backing a stable standard buys you.
Frequently asked
- Which is faster for large-scale scraping, and why?
- How do you attach a different proxy per session in each?
- Which is easier to scale across many machines?
- Are either of them detectable by anti-bot systems out of the box?
- Should I use one of these at all instead of a scraping API?
Weekly briefing – tool launches, legal shifts, market data.
