Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

zendriver-rs is an async-first, undetectable browser-automation library for Rust. It drives a real Chrome instance over the Chrome DevTools Protocol (CDP) directly — no WebDriver shim, no Selenium grid, no JSON wire — and ships with anti-detection patches that pass mainstream fingerprint batteries out of the box.

It is a Rust port of the Python zendriver / nodriver projects, with the API redesigned around Rust's type system: builder patterns where Python uses kwargs, traits where Python uses duck-typed protocols, Result where Python uses exceptions, and explicit lifetimes for query scopes that the borrow checker tracks instead of letting them drift across await points.

Use cases

  • Scraping sites that block headless browsers. The spoofed stealth profile patches navigator.webdriver, the Chrome runtime object, the permissions API, and a half-dozen other tells. Cloudflare Turnstile, PerimeterX, and DataDome challenges pass without manual headers.
  • End-to-end testing of real-world web apps. First-class multi-tab, cross-origin iframe (OOPIF) support, network interception, and a Playwright-style expect() pre-register surface make whole-flow tests expressive without the wire-protocol churn of WebDriver.
  • Browser automation pipelines under load. Tab handles are `Send + Sync
    • Clone, the transport is a single Tokio actor, and queries are zero-copy &strselectors — comfortable inside anytokio::spawn`'d worker pool.
  • Drop-in replacement for chromiumoxide callers who want stealth, multi-tab, and an ergonomic find().css("...").one() query surface instead of hand-rolling Page.querySelector calls.

What makes it different

  • CDP-direct. Every method maps to one or two CDP commands. There is no WebDriver-style adapter layer, so latency is one network round-trip per call — typically under 1 ms on localhost.
  • Undetectable by default. StealthProfile::native is the suggested starting point — UA scrub plus Emulation overrides, no JS bootstrap, no prototype patching. StealthProfile::spoofed adds Navigator-prototype patches that pass sannysoft + areyouheadless. Off-by-default for StealthProfile::off when you want a vanilla browser for reproduction.
  • Async-first. Built on Tokio. Every call returns a Future. No blocking, no block_on, no tokio::task::spawn_blocking. Browser / Tab / Element handles are Clone + Send + Sync so they cross .await boundaries and task spawns without ceremony.
  • Rust-native. Errors are typed via thiserror and surfaced through Result. Selectors are checked at call time, not parsed at startup. Resources clean up via Drop (Chrome subprocess gets SIGTERM when the last Browser clone drops). The borrow checker tracks query scopes for you.

Comparison

featurezendriver-rschromiumoxidethirtyfourfantoccini
TransportCDP-directCDP-directWebDriverWebDriver
Stealth out of the boxyesnonono
Builder-style queriesyespartialnono
Cross-origin iframesyespartialyesyes
Send+Sync handlesyesyesyesyes
Async runtimeTokioasync-std/TokioTokioTokio
Network interceptionyesyeslimitedlimited
Multi-tab orchestrationyesmanualmanualmanual

Comparisons against Playwright + Selenium are covered in Migration from Playwright.

How this book is organized

The API rustdoc on docs.rs/zendriver is the source of truth for the public surface. The book covers the how and why; rustdoc covers the what.