自动化工具 活跃维护

pydoll

autoscrape-labs/pydoll

Pydoll 是一个用于在不使用 WebDriver 的情况下自动化基于 Chromium 浏览器的库,提供逼真的交互。

7131
Stars 标星
411
Forks 分支
37
Watchers 关注
22
Open Issues
HTML
主要语言
MIT
开源协议
83.3 MB
仓库大小
17 天前
最后推送
一键安装扩展 / 插件指令
dsh plugin --profile web add github:autoscrape-labs/pydoll
git clone https://github.com/autoscrape-labs/pydoll.git
git clone git@github.com:autoscrape-labs/pydoll.git
README.md main

英文原文正文为项目原始 README(英文),本站后续会翻译为中文,当前仅剔除图片与无关章节并统一排版。


基于原始 CDP 的 Python 浏览器自动化。
无需 WebDriver,无需驱动进程,支持同步或异步,你的 Playwright 脚本只需一个导入即可运行。

= 3.10">

文档 · 快速开始 · Playwright · 功能 · 支持

[!注]
Pydoll 3 已经到来。 本次发布带来了同步 API、一个通过一次导入即可启用的兼容 Playwright 的层、新的等待方法以及更快的核心。它还带来了一些不兼容的更改,所以在升级之前请阅读升级指南。我们希望听到你的使用反馈:请在问题 #463分享哪些功能可用,哪些功能有问题。

为什么是派娃娃?

  • 无需修补:通过 WebSocket 的直接 CDP 连接。无需驱动程序二进制文件,无 navigator.webdriver 标志,无版本匹配,也无需保持更新的隐身分支。
  • 指纹注入:使用 tab.apply_fingerprint() 让浏览器报告完全一致的身份信息:User-Agent、客户端提示(Client Hints)、navigator、WebGL、canvas、屏幕、字体、时区和语言环境,全都保持一致。覆盖会在 toString 和原型检查中生效,并传播到 Web Workers,所以像 CreepJS 这样的反欺骗检测不会发现异常。
  • 人性化交互:鼠标移动沿贝塞尔曲线,真实的打字和滚动物理效果。根据您的浏览器和 IP 声誉,这通常足以通过如 Cloudflare Turnstile 或 reCAPTCHA v3 之类的行为验证挑战。
  • 同步或异步,完全类型化:from pydoll import Chrome 提供 asyncio API,from pydoll.sync import Chrome 提供阻塞 API,具有相同的名称、方法和默认值。通过 mypy 进行类型检查,支持完整的 IDE 自动完成。
  • 兼容 Playwright:保留您的 Playwright 脚本并更改一个导入。定位器、get_by_role、自动等待、路由、对话框和下载在 Pydoll 的连接下保持其语义。
  • 网络控制:拦截 请求以阻止广告和跟踪器,监控 流量以发现 API,并发起带认证的 HTTP 请求,继承浏览器会话。
  • Shadow DOM 和 iframes:完全支持 shadow roots(包括 closed)和跨域 iframes,并且可以在它们内部使用相同的 find()、query() 和 click()。
  • 结构化提取:定义一个 Pydantic 模型,调用 tab.extract_all(),并获得类型化、验证过的对象。

[!注]
维护者说几句。Pydoll 由一个人维护,我目前有些紧张,所以新版本发布和问题回复可能会比平时更久。项目没有死去,也不会消失;目前开发进展较为平稳。

一个值得努力的目标:一旦项目达到1万颗星,我计划发布对Firefox的支持,这是一个重大工作,将开启全新的可能性。如果你希望看到它实现,那就是它所需要的推动力。

安装

pip install pydoll-python

需要安装 Python 3.10 或更新版本,以及 Google Chrome 或 Microsoft Edge。不需要 WebDriver,不需要下载浏览器,不需要 Node。

快速开始

打开一个页面,根据你向别人描述它们的方式查找元素,并以人性化的时间进行交互:

import asyncio

from pydoll import Chrome, Key

async def google_search(query: str):
    async with Chrome() as browser:
        tab = await browser.start()
        await browser.set_window_maximized()
        await tab.go_to('https://www.google.com')

        search_box = await tab.find(tag_name='textarea', name='q')
        await search_box.type_text(query, humanize=True)
        await tab.keyboard.press(Key.ENTER)

        first_result = await tab.find(tag_name='h3', text='autoscrape-labs/pydoll', timeout=10)
        await first_result.click(humanize=True)
        await asyncio.sleep(5)
        print(f"Page loaded: {await tab.title()}")

asyncio.run(google_search('pydoll site:github.com'))

相同的脚本没有 async:从 pydoll.sync 导入并去掉 await。同步 API 是从异步 API 生成的,所以它不会落后,而且回调仍然有效。

from pydoll.sync import Chrome, Key

with Chrome() as browser:
    tab = browser.start()
    tab.go_to('https://www.google.com')

    tab.find(tag_name='textarea', name='q').type_text('pydoll site:github.com', humanize=True)
    tab.keyboard.press(Key.ENTER)

    tab.find(tag_name='h3', text='autoscrape-labs/pydoll', timeout=10).click(humanize=True)
    print(f"Page loaded: {tab.title()}")

当目标是数据而不是交互时,定义一个模型,并让 Pydoll 提取它,进行类型检查和验证:

import asyncio

from pydoll import Chrome, ExtractionModel, Field

class Quote(ExtractionModel):
    text: str = Field(selector='.text')
    author: str = Field(selector='.author')
    tags: list[str] = Field(selector='.tag')

async def main():
    async with Chrome() as browser:
        tab = await browser.start()
        await tab.go_to('https://quotes.toscrape.com')

        quotes = await tab.extract_all(Quote, scope='.quote', timeout=5)
        for quote in quotes:
            print(f'{quote.author}: {quote.text}')

asyncio.run(main())

入门指南 带你从一个空文件夹到运行脚本;文档中的每个示例都有同步(Sync)和异步(Async)选项卡。

已经在使用 Playwright?

保留你的脚本。更改一个导入,它就可以在 Pydoll 的 CDP 连接上运行,并在 Pydoll 的隐身模式下工作。

from pydoll.playwright.sync_api import sync_playwright  # was: from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto('https://quotes.toscrape.com/login')

    page.get_by_label('Username').fill('john')
    page.locator('#password').fill('SecretPass123')
    page.get_by_role('button', name='Login').click()
    page.wait_for_url('**/')
    print(page.get_by_role('link', name='Logout').is_visible())
    browser.close()

pydoll.playwright.async_api 是异步版本。该层覆盖了浏览器自动化表面:Playwright、Browser、BrowserContext、Page、Frame、Locator、ElementHandle、Keyboard、Mouse、路由、对话框和下载。expect() 断言、夹具、追踪和测试运行器不在覆盖范围内。当你需要 Playwright 做不到的功能时,page.tab 是底层的 Pydoll Tab:

with page.tab.expect_cloudflare_turnstile():
    page.goto('https://site-with-turnstile.com')

带上你的 Playwright 脚本 是操作指南;Playwright API 是完整兼容矩阵。

保持隐蔽

指纹注入

Pydoll 还可以让浏览器报告不同的身份。tab.apply_fingerprint() 会覆盖指纹脚本读取的表面信息(User-Agent 和 Client Hints、navigator、WebGL、canvas、屏幕、字体、时区和语言环境),并保持这些值彼此一致。

伪造指纹不在于改变数值,而在于不被发现数值被篡改。现代反机器人脚本会检查属性是如何定义的:一个天真的 Object.defineProperty 会留下一个假的 toString、一个原型 getter 应该存在的自有属性,或者被 phantom iframe 或 Web Worker 可以直接识别的覆盖。Pydoll 能解决这个问题:注入的 getter 在 toString 和原型检查下表现为原生,并且相同的身份会在专用、共享和服务工作线程中重放。

它还可以中和无头浏览器的特征,主要是 SwiftShader WebGL 渲染器,这会暴露一个没有 GPU 的浏览器,因此 headless=True 不再是自动暴露。这让普通的谷歌搜索能够在无头模式下运行。(Cloudflare Turnstile 在无头模式下的情况仍在研究中。)

import asyncio

from pydoll import Chrome

from examples.fingerprints import FINGERPRINTS

async def spoof_fingerprint():
    async with Chrome() as browser:
        tab = await browser.start()

        # Apply before navigating: the JS overrides register on every new document.
        await tab.apply_fingerprint(FINGERPRINTS['windows11_rtx3060_nyc'])

        await tab.go_to('https://abrahamjuliot.github.io/creepjs/')
        print('Fingerprint applied.')
        await asyncio.sleep(5)

asyncio.run(spoof_fingerprint())

在我们的测试中,它在每一个指纹和机器人检测套件中都通过了测试,没有被标记:

测试网站 检查内容 结果
CreepJS 谎言检测,原型 / toString 篡改,workers,字体 无检测
SannySoft 无头和机器人信号 无检测
BrowserScan 机器人检测套件 无检测
BrowserLeaks WebGL WebGL 厂商 / 渲染器 / 哈希 无检测
BrowserLeaks JavaScript navigator / JS 环境 无检测
BrowserLeaks Canvas Canvas 指纹 无检测
BrowserLeaks WebRTC WebRTC IP 泄漏 无检测

一个指纹的强度取决于其最薄弱的层。反机器人系统会在所有层中关联信号。如果一个浏览器渲染为 macOS,但其 Accept-Language 显示巴西葡萄牙语,时区显示东京,IP 地理位置却在德国,那么它比一个你从未接触过的浏览器更可疑。apply_fingerprint() 保持它控制的层的一致性,但其他部分由你掌控:配置必须与真实的 Chrome 二进制匹配(网络层 TLS / HTTP2 指纹是真实的,无法伪造)以及出口 IP 或代理的地理位置。浏览器指纹深度解析 和 时区与区域一致性 章节解释了为什么与 IP 不一致的区域会导致被封锁。

[!重要]
Pydoll 不生成或提供指纹。examples/fingerprints.py 中的配置仅作为参考,显示一个配置需要多么一致以及你注入的 FingerprintConfig 结构。请自备指纹。

指纹注入文档

Cloudflare Gateway

Pydoll 处理 Cloudflare Turnstile 的方式与人相同:它找到组件并进行逼真的、人性化的点击。Turnstile 是否接受点击取决于你的浏览器指纹和 IP 声誉,这就是为什么前两节内容先讲。

import asyncio

from pydoll import Chrome

async def solve_turnstile():
    async with Chrome() as browser:
        tab = await browser.start()

        # Waits for the Turnstile widget, performs a realistic click,
        # and continues once it settles.
        async with tab.expect_cloudflare_turnstile():
            await tab.go_to('https://site-with-turnstile.com')

        print('Turnstile handled, continuing...')

asyncio.run(solve_turnstile())

Pydoll 通过逼真、人性化的点击通过 Cloudflare Turnstile 验证。

[!注意]
这不是绕过。Pydoll 执行的点击与真实用户相同;是否通过取决于您的环境(浏览器指纹和 IP 声誉)。详情请参阅 Turnstile 文档。

功能

上面的部分涵盖了大多数人开始时使用的流程。其余内容如下:点击任何项目以展开简短说明、可运行示例以及其完整指南的链接。

结构化数据提取(Pydantic)


使用 Pydantic 模型定义你想要的内容,Pydoll 会将 DOM 直接映射为类型化、经过验证的 Python 对象,无需逐元素手动查询。模型支持 CSS/XPath 自动检测、HTML 属性定位、自定义转换和嵌套模型。

import asyncio

从 pydoll 导入 Chrome、ExtractionModel、Field

class Quote(ExtractionModel):
    text: str = Field(selector='.text', description='引用文本')
    author: str = Field(selector='.author', description='引用者')
    tags: list[str] = Field(selector='.tag', description='标签')

异步 定义 提取_引用():
    异步使用 Chrome() 作为浏览器:
        tab = await browser.start()
        await tab.go_to('https://quotes.toscrape.com')

        quotes = await tab.extract_all(Quote, scope='.quote', timeout=5)

        for q in quotes:
            print(f'{q.author}: {q.text}')  # fully typed, IDE autocomplete works
            print(q.model_dump_json())       # pydantic serialization built-in

asyncio.run(extract_quotes())
人性化鼠标移动


当你传递 humanize=True 时,鼠标操作可以产生类似人类的光标移动:

  • 具有不对称控制点的贝塞尔曲线路径
  • 费茨定律时间,持续时间随距离变化
  • 最小振动速度,呈钟形的速度曲线
  • 生理性震颤,随速度变化的高斯噪声
  • 超调修正,大约 70% 的快速移动会超调然后修正回去
等待 tab.mouse.move(500, 300, humanize=True)
等待 tab.mouse.click(500, 300, humanize=True)
等待 tab.mouse.drag(100, 200, 500, 400, humanize=True)

button = 等待 tab.find(id='submit')
等待 button.click(humanize=True)

# 默认是快速的、非人性化的移动
await tab.mouse.click(500, 300)

鼠标控制文档

Shadow DOM 支持


完整的 Shadow DOM 支持,包括封闭的 shadow roots。由于 Pydoll 在 CDP 级别(低于 JavaScript)操作,closed 模式限制不适用。

shadow = await element.get_shadow_root()
button = await shadow.query('.internal-btn')
await button.click()

# 在页面上发现所有的 shadow roots
shadow_roots = await tab.find_shadow_roots()
对于 shadow_roots 中的 sr:
    checkbox = await sr.query('input[type="checkbox"]', raise_exc=False)
    if checkbox:
        await checkbox.click()

亮点:

  • 封闭的 shadow 根无需变通方法即可工作
  • find_shadow_roots() 可以发现页面上的每个 shadow 根
  • timeout 参数用于轮询直到 shadow 根出现
  • deep=True 可以遍历跨域 iframe(OOPIFs)
  • 在 shadow 根内部使用标准的 find()、query()、click() API

Shadow DOM 文档

HAR 网络记录


在浏览器会话期间记录网络活动,并将其导出为 HAR 1.2。每个条目都保留其请求、响应、头信息、正文和时间,准备好供 DevTools 或任何 HAR 查看器使用。

从 pydoll 导入 Chrome

异步使用 Chrome() 作为浏览器:
    tab = await browser.start()

    异步使用 tab.request.record() 作为 capture:
        await tab.go_to('https://example.com')

    capture.save('flow.har')
    print(f'捕获了 {len(capture.entries)} 个请求')

HAR 记录文档

页面捆绑包


将当前页面及其所有资源(CSS、JS、图片、字体)保存为一个 .zip 压缩包以便离线查看。可选地将所有内容内联到单个 HTML 文件中。

等待 tab.save_bundle('page.zip')
等待 tab.save_bundle('page-inline.zip', inline_assets=True)

截图, PDF & 套餐 文档

混合自动化(UI API)


使用UI自动化来通过登录流程(验证码、JS挑战),然后切换到tab.request进行快速的API调用,这些调用继承完整的浏览器会话:包括Cookie、头信息等所有内容。

# 通过UI登录
await tab.go_to('https://my-site.com/login')
await (await tab.find(id='username')).type_text('user')
await (await tab.find(id='password')).type_text('pass123')
await (await tab.find(id='login-btn')).click()

# 使用浏览器会话进行认证的API调用
response = await tab.request.get('https://my-site.com/api/user/profile')
user_data = response.json()

混合自动化文档

网络拦截与监控


监控 API 流量以进行发现或拦截请求,以阻止广告、跟踪器和不必要的资源。

import asyncio

from pydoll import Chrome, FetchEvent
从 pydoll.protocol.fetch.events 导入 RequestPausedEvent
从 pydoll.protocol.network.types 导入 ErrorReason

异步 定义 阻止_图片():
    异步使用 Chrome() 作为浏览器:
        tab = await browser.start()

        async def block_resource(event: RequestPausedEvent):
            request_id = event['params']['requestId']
            resource_type = event['params']['resourceType']

            if resource_type in ['Image', 'Stylesheet']:
                await tab.fail_request(request_id, ErrorReason.BLOCKED_BY_CLIENT)
            else:
                await tab.continue_request(request_id)

        await tab.enable_fetch_events()
        await tab.on(FetchEvent.REQUEST_PAUSED, block_resource)

        await tab.go_to('https://example.com')
        await asyncio.sleep(3)
        await tab.disable_fetch_events()

asyncio.run(block_images())

网络监控 | 请求拦截

浏览器指纹控制


对浏览器偏好设置的细粒度控制:用于构建一致指纹的数百个内部 Chrome 设置。

来自 pydoll 导入 ChromiumOptions

options = ChromiumOptions()

options.browser_preferences = {
    'profile': {
        'default_content_setting_values': {
            'notifications': 2,
            'geolocation': 2,
        },
        'password_manager_enabled': False
    },
    'intl': {
        'accept_languages': 'en-US,en',
    },
    '浏览器': {
        'check_default_browser': False,
    }
}

浏览器偏好设置指南

并发、上下文和远程连接


同时管理多个标签页和浏览器上下文(隔离的会话)。连接到在 Docker 或远程服务器中运行的浏览器。

import asyncio

从 pydoll 导入 Chrome

异步 def 抓取页面(url, 标签):
    等待 tab.go_to(url)
    返回 await tab.title()

异步定义 并发抓取():
    异步使用 Chrome() 作为浏览器:
        tab_google = await browser.start()
        tab_ddg = await browser.new_tab()

        results = await asyncio.gather(
            scrape_page('https://google.com/', tab_google),
            scrape_page('https://duckduckgo.com/', tab_ddg)
        )
        print(results)

多标签管理 | 远程连接

重试装饰器


@retry 装饰器支持在尝试之间自定义恢复逻辑(例如,刷新页面、轮换代理)和指数退避。

来自 pydoll.decorators 导入 retry
从 pydoll.exceptions 导入 ElementNotFound, NetworkError

@retry(
    max_retries=3,
    exceptions=[ElementNotFound, NetworkError],
    on_retry=my_recovery_function,
    exponential_backoff=True
)
异步定义 scrape_product(self, url: str):
    # 抓取逻辑
    ...

重试装饰器文档


贡献

欢迎任何形式的贡献,无论是提交错误报告、修正文档,还是添加新功能。如果你不确定从哪里开始,可以先打开一个 issue,我们可以一起解决。CONTRIBUTING.md 包含开发环境设置、如何运行测试,以及代码风格和提交规范。目前只有一名维护者,一份清晰的复现步骤或针对性的拉取请求会非常有帮助。

支持

帮助 Pydoll 的几种方法:

  • 在仓库上加星标,这样更多人可以找到它(是的,上面开头的笑话仍然适用)。
  • 报告一个错误或你遇到的粗糙问题。一个好的问题非常有价值。
  • 改进文档页面,或在问题中回答其他人的问题。
  • 如果它能为你的工作节省时间,在 GitHub 上赞助该项目。

以上任何一种方式都可以让项目继续发展。

许可

Pydoll 在 MIT 许可证 下发布。只要保留版权声明,就可以在个人或商业项目中使用它。

本站来源与版权声明
  • 本文标题:pydoll - Pydoll 是一个用于在不使用 WebDriver
  • 本文链接:https://www.cn121.com/automation/autoscrape-labs-pydoll.html
  • 原项目:autoscrape-labs/pydoll 版权归原作者 autoscrape-labs 及贡献者所有
  • 收录信息:本站于 2026-10-27 收录本项目,本页所列协议与仓库指标均为收录当时的状态;该日期之后原项目的版本更新与协议变更,本页不作同步。
  • 开源协议:收录时本项目采用 MIT(查看 LICENSE 原文),本站转载其原始文档(未改动文字,仅剔除图片与无关章节);使用、修改、分发请以该仓库 LICENSE 原文为准。
  • 站点出处:本文首发于 OneTwoOne,收录自 GitHub 开源项目 autoscrape-labs/pydoll。
  • 内容说明:本页正文为原项目 README 原文(英文),本站后续会翻译为中文(当前尚未译出,仅将二级标题译为中文,便于按栏目定位;标题原文可在下方原仓库中查看),仅剔除了图片与赞助等无关章节、并把相对链接改为绝对地址;页首简介为机器翻译自仓库描述。
  • 引用声明:商业转载、第三方聚合或 AI 检索训练引用时,请务必保留以上来源出处、本文永久链接,以及原项目的版权声明与许可信息。
  • 下架通道:若原项目此后变更或收紧了许可协议、或作者/权利人认为本站的收录方式(译文、排版适配、简介翻译等)超出其授权范围,请通过 xyd3302001@163.com 发送下架通知,并附上项目地址与本页链接。本站核实后将第一时间删除本页内容,或改为不复制原文的目录性收录;署名更正等其他要求可一并提出。