zendriver

cdpdriverAGPL-3.0活跃维护

基于 Chrome DevTools 协议的异步网页抓取与自动化框架,能绕过 Cloudflare 等反爬检测,适合需要隐蔽采集的场景。

A blazing fast, async-first, undetectable webscraping/web automation framework based on ultrafunkamsterdam/nodriver. Now with Docker support!

v0.17.1发布于 2026-10-02 02:07 UTC1,473star108fork同步于 2026-11-07 07:31 UTC

怎么上手

由 README 提炼,执行前请确认
pip install zendriver
硬件要求按 README 说明

README 未说明具体硬件要求;需本机安装 Chrome 浏览器,Docker 方案需 Linux 且可 GPU 加速运行非 headless Chrome

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

基于 CDP 的异步浏览器自动化框架,绕过 Selenium 检测,几行 Python 完成抓取

基于 Chrome DevTools 协议的异步网页抓取与自动化框架,能绕过 Cloudflare 等反爬检测,适合需要隐蔽采集的场景。

核心能力

  • 用 Chrome DevTools Protocol 驱动真实浏览器,不走 WebDriver
  • 全异步 Python API,启动和页面操作都基于 asyncio
  • 默认每次运行使用全新 profile,退出时自动清理
  • 支持按选择器或文本查找元素,可穿透 iframe 内容
  • 元素 repr 直接输出 HTML,便于调试

容易踩的坑

需要本机已有可用的 Chrome 浏览器,包本身不带浏览器;Docker 部署需另用 cdpdriver/zendriver-docker 模板且仅支持 Linux;按文本查找默认取第一个匹配,需 best_match 才按文本长度择优

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v0.17.1
  1. v0.17.1最新正式版2026-10-02 · 2026-10-02 02:07 UTC

    Fixed

    • The "Failed to connect to browser" error now suggests sandbox=False instead of no_sandbox=True, which isn't a real option and was silently ignored
    • Tab.wait_for_ready_state now respects timeout even when a document.readyState check never gets a response, instead of hanging forever
  2. v0.17.02026-09-27 · 2026-09-27 02:34 UTC
    查看更新说明

    This is quite a large release, including a number of fixes from first-time contributors (thank you!) as well as ports for all nodriver features and bugfixes that have been added upstream since Zendriver was forked. Additionally, I've managed to remove the last remaining # type: ignore comments, which uncovered a few additional bugs that have also been fixed in this release.

    This release marks two years of Zendriver! Huge thanks to everyone that has contributed so far.

    Fixed

    • Fix ValueError on startup when the lang option is set @bercedev
    • Fix verify_cf() never clicking the Turnstile checkbox on Turnstile markup where the cf-turnstile-response/cf_challenge_response input is rendered as a document-level sibling of the challenge's shadow-DOM host rather than nested inside it. The lookup was scoped to host_element, always found nothing, and verify_cf() returned before ever calling mouse_click. Now queries the input from the document (tab.query_selector) instead. @A-Nolan
    • Fix HTTP method casing to resolve CDP connection issues on some Windows installations @fitudao3788
    • Fix 404 Not Found WebSocket connection error for iframe targets by routing all target connections to /devtools/page/<target_id> in Browser._handle_target_update.
    • Fix Browser.stop() always waiting 3 seconds and then force-killing the browser process, even when the browser had already exited gracefully. The exit check read Popen.returncode, which is only updated by poll()/wait(). @stephanlensky
    • Fix Browser.start() always sleeping for browser_connection_timeout before the first connection attempt. It now tries to connect immediately and only waits between retries. @stephanlensky
    • Fix Browser instances never being garbage collected after Browser.stop(). The asyncio atexit callback and the global registered instances set kept a reference to every browser, and per-target connections were left open. @stephanlensky
    • Fix the connection listener crashing with InvalidStateError when a response arrived for a command that was already cancelled (for example by asyncio.wait_for), which left later commands hanging forever @stephanlensky
    • Fix every received CDP event being kept in memory for the lifetime of its connection @stephanlensky
    • Fix Tab.find() raising ProtocolException: No search session with given id found instead of retrying when the page's document is replaced during the search @stephanlensky
    • Fix await tab raising ValueError: No listener created yet on a tab which no commands were sent to yet, such as one just opened with browser.get(url, new_tab=True) @stephanlensky
    • Fix browser.get(url, new_tab=True) intermittently raising StopIteration when the new tab was not registered yet, and browser.get() returning before the navigation when another target changed in the meantime @stephanlensky
    • Fix Widevine DRM being unavailable on platforms where Chrome installs it through the component updater, such as Windows. Removed the --disable-component-update, --disable-background-networking, --disable-backgrounding-occluded-windows and --disable-renderer-backgrounding default browser arguments @stephanlensky
    • Fix Browser.start() sometimes returning before the browser's initial tab was registered, which made browser.get(url) raise RuntimeError: coroutine raised StopIteration. browser.get(url) now raises a descriptive error if there is no tab to navigate @stephanlensky
    • Fix extensions added with Config.add_extension() not loading on Chrome 137+. The default --disable-features switch was overridden by a second one, since Chrome only uses the last. All --disable-features switches, including user-provided ones, are now merged into one, and --enable-unsafe-extension-debugging is passed when extensions are added @stephanlensky
    • Fix commands hanging forever when the browser connection closed while they were waiting for a response, for example because the browser crashed or `Co
  3. v0.16.02026-08-16 · 2026-08-16 23:21 UTC
    查看更新说明

    Fixed

    • Fix pattern argument in CookieJar.save @Lyesbcb

    Added

    • Add support for Microsoft Edge browser @mokurin000
  4. v0.15.52026-07-15 · 2026-07-15 22:00 UTC
    查看更新说明

    Fixed

    • Fix SyntaxError on Python 3.14 in Connection._register_handlers. PEP 765 makes continue/break/return that exits a finally block a SyntaxError (was a SyntaxWarning in 3.13), so zendriver/core/connection.py failed to import under 3.14. Removed the no-op finally: continue (the loop already falls through to the next iteration, and the enclosing bare except: catches everything, so nothing changed behaviorally). Fixes #252
  5. v0.15.42026-06-27 · 2026-06-27 18:05 UTC
    查看更新说明

    Fixed

    • Fix query_selector_all and select_all(include_frames=True) not searching nested iframes. CDP's querySelectorAll only queries within a single document boundary, so elements inside nested iframes were never found. Now walks the full DOM tree to collect each iframe's content_document and queries them individually. Also adds a guard for cross-origin iframes where content_document is None. @chronoAP
    • Fix Connection._register_handlers reenabling already manually enabled domains @S-Tarr
    • Fix flakey behavior in api-reponses-tutorial-2 tutorial @S-Tarr

    Changed

    • Updated CDP models @nathanfallet

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    Zendriver ✌️

    License Pypi Version Issues Pull Requests codecov

    This package is a fork of ultrafunkamsterdam/nodriver, created to add new features, compile unmerged bugfixes, and increase community engagement.

    Zendriver is a blazing fast, async-first, undetectable webscraping/web automation framework implemented using the Chrome Devtools Protocol. Visit websites, scrape content, and run JavaScript using a real browser (no Selenium/Webdriver) all with just a few lines of Python.

    Docker support is here! Check out cdpdriver/zendriver-docker for an example of how to run Zendriver with a real, GPU-accelerated browser (not headless) in a Docker container. (Linux-only)

    Features

    • Undetectable - Zendriver uses the Chrome Devtools Protocol instead of Selenium/WebDriver, making it (almost) impossible to detect
    • Blazing fast - Chrome Devtools Protocol is fast, much faster than previous Selenium/WebDriver solutions. CDP combined with an async Python API makes Zendriver highly performant.
    • Feature complete and easy to use - Packed with allowing you to get up and running in just a few lines of code.
    • First-class Docker support - Traditionally, browser automation has been incredibly difficult to package with Docker, especially if you want to run real, GPU-accelerated Chrome (not headless). Now, deploying with Docker is easier than ever using the officially supported zendriver-docker project template.
    • Automatic cookie and profile management - By default, uses fresh profile on each run, cleaning up on exit. Or, save and load cookies to a file to avoid repeating tedious login steps.
    • Smart element lookup - Find elements selector or text, including iframe content. This could also be used as wait condition for a element to appear, since it will retry for the duration of timeout until found. Single element lookup by text using tab.find() accepts a best_match flag, which will not naively return the first match, but will match candidates by closest matching text length.
    • Easy debugging - Descriptive repr for elements, which represents the element as HTML, makes debugging much easier.

    Installation

    To install, simply use pip (or your favorite package manager):

    pip install zendriver
    # or uv add zendriver, poetry add zendriver, etc.
    

    Usage

    Example for visiting https://www.browserscan.net/bot-detection and saving a screenshot of the results:

    import asyncio
    
    import zendriver as zd
    
    
    async def main():
        browser = await zd.start()
        page = await browser.get("https://www.browserscan.net/bot-detection")
        await page.save_screenshot("browserscan.png")
        await browser.stop()
    
    
    if __name__ == "__main__":
        asyncio.run(main())
    

    Check out the Quickstart for more information and examples.

    Rationale for the fork

    Zendriver remains committed to nodriver's goals of staying undetected for all modern anti-bot solutions and also keeps with the batteries-included approach of its predecessor. Unfortunately, contributions to the original nodriver repo are heavily restricted, making it difficult to submit issues or pull requests. At the time of writing, there are several pull requests open to fix critical bugs which have beeen left unaddressed for many months.

    Zendriver aims to change this by:

    1. Including open pull requests in the original nodriver repo as part of the initial release
    2. Modernizing the development process to include static analysis tools such as ruff and mypy, reducing the number of easy-to-catch bugs which make it through in the future
    3. Opening up the issue tracker and pull requests for community contributions, allowing the project to continue to grow along with its community.

    With these changes in place, we hope to further development of state-of-the-art open-source web automation tools even further, helping to once again make the web truly open for all.

    Contributing

    Contributions of all types are always welcome! Please see CONTRIBUTING.md for details on how to contribute.

    Getting additional help

    If you have a question, bug report, or want to make a general inquiry about the project, please create a new GitHub issue. If you are having a problem with Zendriver, please make sure to include your operating system, Chrome version, code example demonstrating the issue, and any other information that may be relevant.

    Questions directed to any personal accounts outside of GitHub will be ignored.