nodriver

Async browser automation and web scraping that talks directly to Chromium via CDP, with no Selenium, WebDriver, or ChromeDriver required.

Library
PyPI
v0.50.3
4,728stars
GNU AGPLv3

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
41/100Fair
Development Activity12
Maintenance0
Community64
Maturity48
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
64/100Good
Architecture72
Code Quality35
Innovation75
Learning Curve75

nodriver is the official successor to undetected-chromedriver, rewritten from the ground up as a fully asynchronous Python library for browser automation and web scraping. Instead of driving a browser through Selenium and a WebDriver binary, it speaks directly to Chromium-based browsers (Chrome, Chromium, Edge, Brave) over the Chrome DevTools Protocol (CDP), which removes an entire layer of indirection and gives it both a performance boost and better resistance against anti-bot systems like Cloudflare, Imperva, and hCaptcha.

The library leans hard into convenience: await nodriver.start() launches a browser with best-practice defaults and a disposable profile in a single line, while element lookup helpers like tab.find(), tab.select(), and tab.xpath() handle common wait-for-element and fuzzy-text-match patterns that would otherwise take many lines of explicit-wait boilerplate in Selenium or Playwright.

What You Get

  • One-line browser startup via await nodriver.start() with sensible, best-practice default configuration and automatic profile cleanup on exit
  • No chromedriver binary or Selenium dependency — direct CDP communication over websockets
  • Smart element lookup with tab.find(), tab.find_all(), tab.select(), tab.select_all(), and tab.xpath(), including matching inside iframes
  • Built-in tab.cf_verify() helper that locates and clicks Cloudflare’s “verify you are human” checkbox
  • Cookie save/load to file so login sessions can be reused across runs
  • Ability to attach to an already-running Chrome debug session, or upgrade an existing undetected-chromedriver instance in place

Common Use Cases

  • Scraping sites protected by Cloudflare, Imperva, or similar anti-bot/WAF systems that block classic Selenium/WebDriver traffic
  • High-throughput, fully asynchronous scraping pipelines where blocking WebDriver calls would bottleneck concurrency
  • Form-filling and account-creation automation that needs resilient, fuzzy element matching instead of brittle CSS selectors
  • Interactive, prototyping-style browser sessions (e.g. in IPython) thanks to the low-boilerplate one-line startup
  • Debugging and inspecting a running browser tab via open_external_debugger() without breaking the automation connection

Under The Hood

Architecture nodriver is layered on top of a large set of auto-generated per-domain CDP protocol bindings under nodriver/cdp/ (accessibility, dom, network, target, and dozens more), which the runtime layer in nodriver/core/ builds on. connection.py implements the websocket transport and a Transaction/ProtocolException model for matching async requests to CDP responses; browser.py defines Browser, which subclasses Connection and owns the browser subprocess lifecycle; tab.py defines Tab (itself a Connection subclass) representing an individual page, window, or iframe target, with IFrame further subclassing Tab; and element.py wraps CDP DOM nodes in a Element convenience object for querying and interaction. The design cleanly separates generated protocol code from hand-written transport and page-abstraction layers, though the large util.py (over 5,000 lines, mixing free helper functions with a ProxyForwarder class) blurs that separation somewhat — changing core Connection request/response handling would ripple through both Browser and every Tab/IFrame instance, since both inherit from it directly.

Tech Stack The library targets Python 3.9+ and is built entirely on asyncio. Its only runtime dependencies are websockets (>=14, for the CDP transport), mss (for screenshot/screen capture), and deprecated (for deprecation warnings) — there is no web framework, ORM, or database layer, since the project’s entire surface is browser process management and CDP messaging. Packaging uses a standard pyproject.toml/setuptools PEP 517 build. Documentation is built with Sphinx (the furo theme plus sphinx_autodoc_typehints) and auto-deployed to GitHub Pages via a GitHub Actions workflow on every push to main.

Code Quality No test files or test directories exist anywhere in the repository, and the checked-in tox.ini is explicit about this: its [testenv], flake8, and pytest sections are all commented out, leaving only inert scaffolding. There is no CONTRIBUTING guide and no CI job that runs tests or lints. black and isort are listed as optional dev dependencies but nothing in CI enforces their use. Docstrings are present on a good portion of public methods across tab.py and element.py, and type hints appear throughout function signatures, but neither is applied exhaustively, and error handling relies on a single broad ProtocolException rather than a typed exception hierarchy.

API Design The public API favors extremely low boilerplate: a bare await nodriver.start() is enough to get a working browser instance with reasonable defaults, and higher-level helpers like tab.find("accept all", best_match=True) do fuzzy text matching across the visible DOM (including iframes) rather than requiring an exact selector, which meaningfully lowers the amount of code needed for common scraping and form-automation flows. The tradeoff is a fairly wide, loosely-typed surface (Config accepts arbitrary **kwargs forwarded to the browser process) and thin narrative documentation beyond the README and auto-generated API reference pages.

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