<img src="https://github.com/user-attachments/assets/219f2dbc-37ed-4aea-a289-ba39cdbb335d" alt="Pydoll Logo" />
<a href="https://github.com/autoscrape-labs/pydoll/stargazers"><img src="https://img.shields.io/github/stars/autoscrape-labs/pydoll?style=social"></a>
<a href="https://codecov.io/gh/autoscrape-labs/pydoll" >
<img src="https://codecov.io/gh/autoscrape-labs/pydoll/graph/badge.svg?token=40I938OGM9"/>
</a>
<img src="https://github.com/thalissonvs/pydoll/actions/workflows/tests.yml/badge.svg" alt="Tests">
<img src="https://github.com/thalissonvs/pydoll/actions/workflows/ruff-ci.yml/badge.svg" alt="Ruff CI">
<img src="https://github.com/thalissonvs/pydoll/actions/workflows/mypy.yml/badge.svg" alt="MyPy CI">
<img src="https://img.shields.io/badge/python-%3E%3D3.10-blue" alt="Python >= 3.10">
<a href="https://deepwiki.com/autoscrape-labs/pydoll"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
📖 文档 • 🚀 快速上手 • ⚡ 高级特性 • 🤝 贡献 • 💖 赞助我
设想以下场景:你需要实现浏览器任务的自动化操作——无论是测试Web应用程序、从网站采集数据,还是批量处理重复性流程。传统方法往往需要配置外部驱动程序、进行复杂的系统设置,还可能面临诸多兼容性问题。
Pydoll的诞生就是解决这些问题!!!
Pydoll 采用全新设计理念,从零构建,直接对接 Chrome DevTools Protocol(CDP),无需依赖外部驱动。 这种精简的实现方式,结合高度拟真的点击、导航及元素交互机制,使其行为与真实用户几乎毫无区别。
我们坚信,真正强大的自动化工具,不应让用户困于繁琐的配置学习,也不该让用户疲于应对反爬系统的风控。使用Pydoll,你只需专注核心业务逻辑——让自动化回归本质,而非纠缠于底层技术细节或防护机制。
没有星星,就没有Bug修复。开玩笑的(也许)
现在你可以控制页面滚动,支持平滑动画并自动等待完成:
from pydoll.constants import ScrollPosition
# 带平滑动画向下滚动(等待完成)
await tab.scroll.by(ScrollPosition.DOWN, 500, smooth=True)
# 导航至特定位置
await tab.scroll.to_bottom(smooth=True)
await tab.scroll.to_top(smooth=True)
# 需要速度时的即时滚动
await tab.scroll.by(ScrollPosition.UP, 300, smooth=False)
不同于立即返回的 execute_script("window.scrollBy(...)"),滚动API使用CDP的awaitPromise等待浏览器的scrollend事件,确保后续操作仅在滚动完全完成后执行。非常适合截取屏幕截图、加载延迟内容或创建真实的阅读模式。
全新的 KeyboardAPI 为页面级别的所有键盘交互提供了简洁、集中的接口:
from pydoll.constants import Key
# 按单个键
await tab.keyboard.press(Key.ENTER)
await tab.keyboard.press(Key.TAB)
# 使用快捷键/组合键(最多3个键)
await tab.keyboard.hotkey(Key.CONTROL, Key.A) # 全选(有效!)
await tab.keyboard.hotkey(Key.CONTROL, Key.C) # 复制(有效!)
await tab.keyboard.hotkey(Key.CONTROL, Key.SHIFT, Key.ARROWRIGHT) # 向右选择单词
# 复杂序列的手动控制
await tab.keyboard.down(Key.SHIFT)
await tab.keyboard.press(Key.ARROWRIGHT) # 按住 Shift 选择文本
await tab.keyboard.up(Key.SHIFT)
主要改进:
- 集中化:所有键盘操作通过 tab.keyboard 访问
- 智能修饰键检测:快捷键自动检测并应用修饰键(Ctrl、Shift、Alt、Meta)
- 完整按键支持:26个字母(A-Z)、10个数字(0-9)、所有功能键、数字键盘和特殊键
- 页面级快捷键:适用于 Ctrl+C、Ctrl+V、Ctrl+A 等(由于 CDP 限制,浏览器 UI 快捷键不起作用)
⚠️ CDP 限制: 浏览器 UI 快捷键(如 Ctrl+T 打开新标签,F12 打开开发者工具)通过 CDP 无法使用。请改用 Pydoll 的方法:
await browser.new_tab()、await tab.close()。
使用 @retry 装饰器将脆弱的脚本转变为强大的生产级爬虫。通过指数退避和自定义恢复策略,自动从网络故障、超时和临时错误中恢复:
import asyncio
from pydoll.browser.chromium import Chrome
from pydoll.decorators import retry
from pydoll.exceptions import ElementNotFound, NetworkError
class ProductScraper:
def __init__(self):
self.tab = None
self.retry_count = 0
# 在每次重试前执行的恢复回调
async def recover_from_failure(self):
self.retry_count += 1
print(f"尝试 {self.retry_count} 失败。恢复中...")
# 刷新页面并恢复状态
if self.tab:
await self.tab.refresh()
await asyncio.sleep(2)
@retry(
max_retries=3,
exceptions=[ElementNotFound, NetworkError],
on_retry=recover_from_failure, # 执行恢复逻辑
delay=2.0,
exponential_backoff=True
)
async def scrape_product(self, url: str):
if not self.tab:
browser = Chrome()
self.tab = await browser.start()
await self.tab.go_to(url)
title = await self.tab.find(class_name='product-title', timeout=5)
return await title.text
强大功能: - 智能重试逻辑:仅对您定义的特定异常重试 - 指数退避:逐步增加等待时间(1秒 → 2秒 → 4秒 → 8秒) - 恢复回调:在重试之间执行自定义逻辑(刷新页面、切换代理、重启浏览器) - 生产验证:自信地处理真实世界爬虫的混乱情况
非常适合处理速率限制、网络不稳定、动态内容加载和验证码检测。将不可靠的爬虫转变为防弹自动化。
现在你可以使用浏览器的 WebSocket 地址直接连接到已运行的实例,并立即使用完整的 Pydoll API:
from pydoll.browser.chromium import Chrome
chrome = Chrome()
tab = await chrome.connect('ws://YOUR_HOST:9222/devtools/browser/XXXX')
# 直接开干:导航、元素自动化、请求、事件…
await tab.go_to('https://example.com')
title = await tab.execute_script('return document.title')
print(title)
这让你可以轻松对接远程/CI 浏览器、容器或共享调试目标——无需本地启动,只需指向 WS 端点即可自动化。
两个让复杂布局遍历更优雅的小助手:
# 获取容器的直接子元素
container = await tab.find(id='cards')
cards = await container.get_children_elements(max_depth=1)
# 想更深入?这将返回子元素的子元素(以此类推)
elements = await container.get_children_elements(max_depth=2)
# 在横向列表中无痛遍历兄弟元素
active = await tab.find(class_name='item--active')
siblings = await active.get_siblings_elements()
print(len(cards), len(siblings))
用更少样板代码表达更多意图,特别适合动态网格、列表与菜单的场景,让抓取/自动化逻辑更清晰、更可读。
wait_until(...) 用于等待元素状态,使用更简单:# 等待元素变为可见,直到超时
await element.wait_until(is_visible=True, timeout=5)
# 等待元素变为可交互(可见、位于顶层并可接收事件)
await element.wait_until(is_interactable=True, timeout=10)
WebElement 方法现已公开:is_visible()is_interactable()is_on_top()execute_script(script: str, return_by_value: bool = False)# 使用 JS 高亮元素
await element.execute_script("this.style.outline='2px solid #22d3ee'")
# 校验状态
visible = await element.is_visible()
interactable = await element.is_interactable()
on_top = await element.is_on_top()
以上新增能力能显著简化“等待+验证”场景,降低自动化过程中的不稳定性,使用例更可预测。
你是否曾经希望能够发出自动继承浏览器所有会话状态的 HTTP 请求?现在你可以了!
tab.request 属性为你提供了一个美观的 requests 风格接口,可在浏览器的 JavaScript 上下文中直接执行 HTTP 调用。这意味着每个请求都会自动获得 cookies、身份验证标头、CORS 策略和会话状态,就像浏览器本身发出请求一样。
混合自动化的完美选择:
# 使用 PyDoll 正常导航到网站并登录
await tab.go_to('https://example.com/login')
await (await tab.find(id='username')).type_text('user@example.com')
await (await tab.find(id='password')).type_text('password')
await (await tab.find(id='login-btn')).click()
# 现在发出继承已登录会话的 API 调用!
response = await tab.request.get('https://example.com/api/user/profile')
user_data = response.json()
# 在保持身份验证的同时 POST 数据
response = await tab.request.post(
'https://example.com/api/settings',
json={'theme': 'dark', 'notifications': True}
)
# 以不同格式访问响应内容
raw_data = response.content
text_data = response.text
json_data = response.json()
# 检查设置的 cookies
for cookie in response.cookies:
print(f"Cookie: {cookie['name']} = {cookie['value']}")
# 向你的请求添加自定义标头
headers = [
{'name': 'X-Custom-Header', 'value': 'my-value'},
{'name': 'X-API-Version', 'value': '2.0'}
]
await tab.request.get('https://api.example.com/data', headers=headers)
为什么这很棒:
- 无需会话切换 - 请求自动继承浏览器 cookies
- CORS 无缝工作 - 请求遵循浏览器安全策略
- 现代 SPA 的完美选择 - 无缝混合 UI 自动化与 API 调用
- 身份验证变得简单 - 通过 UI 登录一次,然后调用 API
- 混合工作流 - 为每个步骤使用最佳工具(UI 或 API)
这为需要浏览器交互和 API 效率的自动化场景开启了令人难以置信的可能性!
想要完全自定义 Chrome 的行为?现在你可以控制一切!
新的 browser_preferences 系统让你可以访问数百个之前无法通过编程方式更改的内部 Chrome 设置。我们说的是远超命令行标志的深度浏览器自定义!
可能性是无限的:
options = ChromiumOptions()
# 创建完美的自动化环境
options.browser_preferences = {
'download': {
'default_directory': '/tmp/downloads',
'prompt_for_download': False,
'directory_upgrade': True,
'extensions_to_open': '' # 不自动打开任何下载
},
'profile': {
'default_content_setting_values': {
'notifications': 2, # 阻止所有通知
'geolocation': 2, # 阻止位置请求
'media_stream_camera': 2, # 阻止摄像头访问
'media_stream_mic': 2, # 阻止麦克风访问
'popups': 1 # 允许弹窗(对自动化有用)
},
'password_manager_enabled': False, # 禁用密码提示
'exit_type': 'Normal' # 始终正常退出
},
'intl': {
'accept_languages': 'zh-CN,zh,en-US,en',
'charset_default': 'UTF-8'
},
'browser': {
'check_default_browser': False, # 不询问默认浏览器
'show_update_promotion_infobar': False
}
}
# 或使用便捷的辅助方法
options.set_default_download_directory('/tmp/downloads')
options.set_accept_languages('zh-CN,zh,en-US,en')
options.prompt_for_download = False
实际应用的强大示例: - 静默下载 - 无提示、无对话框,只有自动化下载 - 阻止所有干扰 - 通知、弹窗、摄像头请求,应有尽有 - CI/CD 的完美选择 - 禁用更新检查、默认浏览器提示、崩溃报告 - 多区域测试 - 即时更改语言、时区和区域设置 - 安全加固 - 锁定权限并禁用不必要的功能 - 高级指纹控制 - 修改浏览器安装日期、参与历史和行为模式
用于隐蔽自动化的指纹自定义:
import time
# 模拟一个已经存在几个月的浏览器
fake_engagement_time = int(time.time()) - (7 * 24 * 60 * 60) # 7天前
options.browser_preferences = {
'settings': {
'touchpad': {
'natural_scroll': True,
}
},
'profile': {
'last_engagement_time': fake_engagement_time,
'exit_type': 'Normal',
'exited_cleanly': True
},
'newtab_page_location_override': 'https://www.google.com',
'session': {
'restore_on_startup': 1, # 恢复上次会话
'startup_urls': ['https://www.google.com']
}
}
这种控制级别以前只有 Chrome 扩展开发者才能使用 - 现在它在你的自动化工具包中!
查看文档了解更多详情。
get_parent_element() 方法检索任何 WebElement 的父元素,使导航 DOM 结构更加容易:
element = await tab.find(id='button')
parent = await element.get_parent_element()
添加到 ChromiumOptions 来控制浏览器启动可以花费多长时间。在较慢的机器或 CI 环境中很有用。
options = ChromiumOptions()
options.start_timeout = 20 # 等待 20 秒
还在为不稳定的下载流程、丢失的文件或混乱的事件监听而头疼吗?tab.expect_download() 来了:一种可靠、简洁的下载方式。
file_path一个“开箱即用”的小示例:
import asyncio
from pathlib import Path
from pydoll.browser import Chrome
async def download_report():
async with Chrome() as browser:
tab = await browser.start()
await tab.go_to('https://example.com/reports')
target_dir = Path('/tmp/my-downloads')
async with tab.expect_download(keep_file_at=target_dir, timeout=10) as dl:
# 触发页面上的下载(按钮/链接等)
await (await tab.find(text='Download latest report')).click()
# 等待完成并读取内容
data = await dl.read_bytes()
print(f"已下载 {len(data)} 字节,保存至: {dl.file_path}")
asyncio.run(download_report())
想要“零成本清理”?不传 keep_file_at 即可——我们会创建临时目录,并在上下文退出后自动清理。对测试场景非常友好。
pip install pydoll-python
就这么简单!安装即用,马上开始自动化
让我们从一个实际例子开始:一个自动执行谷歌搜索并点击第一个结果的自动化流程。通过这个示例,你可以了解该库的工作原理,以及如何开始将日常任务自动化。
import asyncio
from pydoll.browser import Chrome
from pydoll.constants import Key
async def google_search(query: str):
async with Chrome() as browser:
tab = await browser.start()
await tab.go_to('https://www.google.com')
search_box = await tab.find(tag_name='textarea', name='q')
await search_box.insert_text(query)
await tab.keyboard.press(Key.ENTER)
await (await tab.find(
tag_name='h3',
text='autoscrape-labs/pydoll',
timeout=10,
)).click()
await tab.find(id='repository-container-header', timeout=10)
asyncio.run(google_search('pydoll site:github.com'))
无需任何配置,只需一个简单脚本,我们就能完成一次完整的谷歌搜索! 好了,现在让我们看看如何从网页中提取数据,依然沿用之前的示例。
假设在以下代码中,我们已经进入了 Pydoll 项目页面。我们需要提取以下信息:
description = await (await tab.query(
'//h2[contains(text(), "About")]/following-sibling::p',
timeout=10,
)).text
下面让我们来理解这条查询语句的作用:
//h2[contains(text(), "About")] - 选择第一个包含"About"的 <h2> 标签/following-sibling::p - 选择第一个在<h2> 标签之后的``标签
然后你可以获取到剩下的数据:
number_of_stars = await (await tab.find(
id='repo-stars-counter-star'
)).text
number_of_forks = await (await tab.find(
id='repo-network-counter'
)).text
number_of_issues = await (await tab.find(
id='issues-repo-tab-count',
)).text
number_of_pull_requests = await (await tab.find(
id='pull-requests-repo-tab-count',
)).text
data = {
'description': description,
'number_of_stars': number_of_stars,
'number_of_forks': number_of_forks,
'number_of_issues': number_of_issues,
'number_of_pull_requests': number_of_pull_requests,
}
print(data)
下图展示了本次自动化任务的执行速度与结果。 (为演示需要,浏览器界面未显示。)

短短5秒内,我们就成功提取了所需数据!
这就是使用Pydoll进行自动化所能达到的速度。
接下来我们来看一个你可能经常遇到的场景:类似Cloudflare的验证码防护。
Pydoll提供了相应的处理方法,但需要说明的是,正如前文所述,其有效性会受到多种因素影响。
下面的代码展示了一个完整的Cloudflare验证码处理示例。
```python import asyncio
from pydoll.browser import Chrome from pydoll.constants import By
async def cloudflare_example(): async with Chrome() as browser: tab = await browser.start() async with tab.expect_and_bypass_cloudflare_captcha(): await tab.
$ claude mcp add pydoll \
-- python -m otcore.mcp_server <graph>