undetected-chromedriver

A drop-in Selenium Chrome WebDriver replacement that patches the chromedriver binary and browser fingerprint to evade bot-mitigation systems.

Library
PyPI
v3.5.5
12,826stars
GNU GPLv3

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
44/100Fair
Development Activity0
Maintenance0
Community76
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
50/100Fair
Architecture55
Code Quality30
Innovation60
Learning Curve55

undetected-chromedriver is a Python package that subclasses Selenium’s Chrome WebDriver to automatically download, patch, and drive a chromedriver binary that anti-bot services like Cloudflare, Distil, Imperva, and DataDome don’t flag as automation. It works at two layers at once: it rewrites the injected $cdc_ marker bytes inside the chromedriver executable itself so services can’t fingerprint the binary, and it uses the Chrome DevTools Protocol to mask navigator.webdriver, spoof the user-agent, and stub out window.chrome properties that scripts probe for in headless mode.

Because Chrome is a subclass of selenium.webdriver.chrome.webdriver.WebDriver, existing Selenium code mostly works unchanged after swapping the import — driver = uc.Chrome() handles version detection, binary download, and patching automatically, with an optional version_main override for pinning an older Chrome release. It also exposes lower-level hooks (add_cdp_listener, find_elements_recursive, a Reactor for wire-protocol events) for scraping and automation workflows that need to inspect network traffic or search across iframes.

What You Get

  • A Chrome class that is a drop-in subclass of Selenium’s own Chrome WebDriver, so most existing Selenium code needs only an import swap
  • Automatic chromedriver version detection, download, and byte-level patching of the injected automation markers, cached under a per-OS data directory
  • CDP-level runtime patching in headless mode: masks navigator.webdriver, spoofs the user-agent string, and stubs window.chrome.app/window.chrome.runtime
  • A Reactor/CDP pair for subscribing to raw Chrome DevTools Protocol wire events (e.g. Network.requestWillBeSent) via add_cdp_listener
  • A find_elements_recursive generator that searches the main frame and all iframes for matching elements without going stale

Common Use Cases

  • Scraping sites protected by Cloudflare, Distil, Imperva, or DataDome that block the standard Selenium/chromedriver signature
  • Automated QA or monitoring scripts against sites with aggressive bot-mitigation that would otherwise serve a CAPTCHA to stock Selenium
  • Headless scraping pipelines that need the extra navigator.webdriver and user-agent masking headless mode applies
  • Debugging or instrumenting page network traffic during automation via the CDP event listener API

Under The Hood

Architecture The package is a single flat module (undetected_chromedriver/) built around one wide class, Chrome(selenium.webdriver.chrome.webdriver.WebDriver), whose ~900-line __init__.py constructor (around 30 keyword arguments) does binary patching, temp-profile setup, subprocess spawning, and headless fingerprint-patching all in one place. Supporting concerns are split into separate classes: Patcher (patcher.py) owns chromedriver version resolution, download, and binary patching; ChromeOptions (options.py) extends Selenium’s ChromiumOptions with user-data-dir and preference-merging logic; Reactor/CDP (reactor.py, cdp.py) subscribe to raw DevTools Protocol wire events; and dprocess.py/devtool.py handle detached-process spawning and devtool discovery. Composition is used for these auxiliary pieces, but the central Chrome class itself is a large, multi-concern constructor rather than a layered pipeline — a change to Selenium’s own WebDriver constructor signature would ripple through most of this file directly.

Tech Stack Pure Python 3.6+, distributed via setup.py/setuptools with no build step. Runtime dependencies are Selenium (>=4.18.1) as the base WebDriver, requests (>=2.31.0) for release-metadata lookups, websockets (>=12.0) for the CDP wire connection, and packaging (>=23.0) for version comparisons. There is no async framework or web framework involved; the target runtime is any machine with a Chrome/Chromium binary installed, and CI matrix-tests Python 3.8-3.11 against a real installed Chrome via browser-actions/setup-chrome.

Code Quality There is no unit or integration test suite with assertions — the only executable check is example/test_workflow.py, a functional smoke script that launches a real browser against live anti-bot test sites and is run once per Python version in CI, with no pass/fail assertions beyond the process not crashing. No linter, formatter, or type-checker configuration is present in the repo. Error handling leans on broad, silent except:/except Exception: blocks in several places (driver teardown, patcher cleanup), which can mask real failures rather than surfacing them. Naming and structure otherwise follow Selenium’s own conventions closely, since the class is a direct subclass of Selenium’s WebDriver.

What Makes It Unique Most Selenium/Playwright/Puppeteer “stealth” add-ons patch only the JavaScript runtime layer. This package additionally patches the chromedriver binary itself at the byte level, rewriting the injected automation marker Chrome’s own detection logic looks for, before layering the same kind of CDP-based JS fingerprint masking (navigator.webdriver, user-agent, window.chrome stubs) on top for headless sessions. That binary-plus-runtime combination is a genuinely distinctive technical choice relative to typical stealth plugins, even though the underlying evasion techniques (patching detection markers, masking navigator properties) are conceptually similar to what tools like playwright-stealth or puppeteer-extra-plugin-stealth do at the JS layer alone.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search