英文原文正文为项目原始 README(英文),本站后续会翻译为中文,当前仅剔除图片与无关章节并统一排版。
基于原始 CDP 的 Python 浏览器自动化。
无需 WebDriver,无需驱动进程,支持同步或异步,你的 Playwright 脚本只需一个导入即可运行。
文档 · 快速开始 · 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提供asyncioAPI,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
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)} 个请求')
页面捆绑包
将当前页面及其所有资源(CSS、JS、图片、字体)保存为一个 .zip 压缩包以便离线查看。可选地将所有内容内联到单个 HTML 文件中。
等待 tab.save_bundle('page.zip')
等待 tab.save_bundle('page-inline.zip', inline_assets=True)
混合自动化(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 发送下架通知,并附上项目地址与本页链接。本站核实后将第一时间删除本页内容,或改为不复制原文的目录性收录;署名更正等其他要求可一并提出。