← All posts
Web Scraping

Python Web Scraping in 2026 — Playwright vs Selenium vs BeautifulSoup

After shipping scrapers with all three, here is a practical breakdown of when Playwright wins, when Selenium still has a place, and when BeautifulSoup is all you need.

Fuad Aliyev June 22, 20269 min readAzərbaycanca oxu: Azərbaycanca

I've shipped over thirty scrapers in the last few years — from a tiny price tracker for a friend's shop to a system pulling millions of pages a day. This is the playbook I actually use to pick between Playwright, Selenium and BeautifulSoup. No theory, just what works.

Short answer if you're in a hurry

  • Static HTML, no JavaScript, no login → requests + BeautifulSoup
  • JS-rendered page, no aggressive anti-bot → Playwright
  • Tough anti-bot (Cloudflare, PerimeterX, DataDome) or complex auth flow → Playwright with playwright-stealth (and residential proxies)
  • Old enterprise app that thinks it's still 2012 → Selenium, sadly

BeautifulSoup — still my fastest tool

First thing I do on any new target: right click, View Page Source. If the data I want is in there, I don't need anything fancy. requests to pull the HTML, BeautifulSoup to parse it, done. A hundred lines of code, fifty megs of RAM, fifty thousand pages an hour on a single box.

python
import httpx
from selectolax.parser import HTMLParser

with httpx.Client(timeout=10, headers={"User-Agent": "Mozilla/5.0"}) as client:
    r = client.get("https://example.com/listings")
    tree = HTMLParser(r.text)
    for item in tree.css("article.listing"):
        title = item.css_first("h3").text(strip=True)
        price = item.css_first(".price").text(strip=True)
        print(title, "—", price)

Tiny note — I usually skip BeautifulSoup these days in favour of selectolax. It's a C-backed parser, five to ten times faster, with the same CSS selector API. At a million pages an hour, the difference matters. BeautifulSoup is still great for learning, but selectolax wins in production.

Playwright — my default in 2026

Most modern sites are React or Vue or Next.js — the initial HTML is mostly empty and data shows up after JavaScript runs. Plain requests is useless there. Playwright spins up a real browser (Chromium, Firefox or WebKit), waits for the page to settle, then reads the DOM.

python
from playwright.sync_api import sync_playwright

with sync_playwright() as pw:
    browser = pw.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto("https://example.com/dashboard", wait_until="networkidle")
    page.wait_for_selector("[data-loaded='true']")
    rows = page.eval_on_selector_all(".row", "els => els.map(e => e.innerText)")
    browser.close()

Where Playwright pulls ahead of Selenium: auto-waiting (you write wait_for_selector, the runtime handles the rest), network interception (block ads, trackers, anything heavy), and clean access to the browser's underlying APIs. On one scrape job, the target page fired 47 analytics requests per pageview. I blocked them with route.abort() and tripled my throughput overnight.

Selenium — only for one reason

I've still reached for Selenium a few times — every time for the same reason. Some legacy enterprise ERP or banking app that only works through ActiveX and old Java applets, and the QA team only has Selenium-Java drivers. For a greenfield project, there is no reason to pick Selenium over Playwright in 2026. Playwright is newer, faster, and needs less boilerplate.

Anti-bot — where things get spicy

Cloudflare, DataDome, PerimeterX, Akamai — they spot a vanilla headless browser in milliseconds. Open Playwright the default way and the JS fingerprint screams 'automated'. I usually layer a few things.

  • playwright-stealth plugin — patches obvious tells like navigator.webdriver
  • Residential proxies (Bright Data, Oxylabs) — datacenter IPs are basically worthless
  • Real user-agent rotation — mobile/desktop, Chrome/Safari, actual current versions
  • Human-like timingpage.wait_for_timeout(random.randint(800, 2400)) between actions
  • Mouse movement on the page — just moving the cursor without clicking helps surprisingly often

My default stack today

When I start a fresh scraping project, the default setup is: try httpx + selectolax first. If it doesn't work → Playwright with Chromium. Hit anti-bot → add playwright-stealth, residential proxies, randomised timing. Data goes into Postgres, parallelism handled by a Redis queue, errors tracked in Sentry. Everything runs in Docker and I keep a Grafana board open during big runs.

Note

Quick ethics note: check the target's robots.txt, throttle yourself, and don't be the reason their site falls over. Scraping public data is mostly fine, but how you scrape and what you do with it is where the law and your reputation actually live. Slow down. Be specific. Be polite.

Wrapping up

Web scraping isn't a tool, it's a process. You don't need a single favourite, you need the right one for each job. My rule of thumb: start with the simplest thing that could work, escalate only when forced. If you're working on something specific and want a second pair of eyes, get in touch — I've shipped 30-plus scrapers and I'm happy to take a look.

Need help on a project?

If something in this post hits close to a project you're working on, let's hop on a 30-minute call — I'll come back with concrete advice.