pydoll: 无需 WebDriver 的浏览器自动化 Python 库

2025年10月04日 02:03:00
14767
python组件

项目结构

📌 pydoll autoscrape-labs/pydoll

用于自动化操作 Chromium 内核浏览器的 Python 库。它通过原生 DevTools 协议(CDP)直接控制浏览器,无需依赖 WebDriver,支持绕过验证码、模拟真人交互、网页截图等功能。

项目大小 3.63 MB
涉及语言 Python 94.12% HTML 3.31% JavaScript 1.97% CSS 0.60%
许可协议 MIT License

Pydoll Logo

Pydoll: Automate the Web, Naturally

Tests Ruff CI MyPy CI Python >= 3.10 Ask DeepWiki

📖 Documentation • 🚀 Getting Started • ⚡ Advanced Features • 🤝 Contributing • 💖 Support My Work

想象一下以下场景:您需要在浏览器中自动执行任务。也许它是测试 Web 应用程序、从站点收集数据,甚至自动化重复过程。通常,这涉及使用外部驱动程序、复杂的配置和许多兼容性问题。

Pydoll 就是为了解决这些问题而诞生的。

Pydoll 采用不同的理念从头开始构建,直接连接到 Chrome DevTools 协议 (CDP),无需外部驱动程序。这种干净的实现以及单击、导航和与元素交互的逼真方式使其与真实用户几乎没有区别。

We believe that powerful automation shouldn’t require you to become an expert in configuration or constantly fight with bot protection systems. With Pydoll, you can focus 上what really matters: your automation logic, not the underlying complexity or protection systems.

做一个好人。给它一个星 ⭐

没有星星,没有修复错误。开个玩笑(也许)

🌟 是什么让 Pydoll 与众不同?

  • 零 Webdrivers:告别 webdriver 兼容性问题
  • 类人交互引擎:能够传递行为验证码,如 reCAPTCHA v3 或 Turnstile,具体取决于 IP 信誉和交互模式
  • 异步性能:用于高速自动化和多个同时任务
  • 人性化互动:模**实用户行为
  • 单纯:使用 Pydoll,您只需安装即可实现自动化。

最新消息

通过 WebSocket 进行远程连接 — 从任何地方控制任何 Chrome!

你要求它,我们交付了。您现在可以通过其 WebSocket 地址远程连接到已经运行的浏览器,并立即使用完整的 Pydoll API。

1
2
3
4
5
6
7
8
9
from pydoll.browser.chromium import Chrome
 
chrome = Chrome()
tab = await chrome.connect('ws://YOUR_HOST:9222/devtools/browser/XXXX')
 
# Full power unlocked: navigation, element automation, requests, events…
await tab.go_to('https://example.com')
title = await tab.execute_script('return document.title')
print(title)

这使得在远程/CI 浏览器、容器或共享调试目标上运行 Pydoll 变得毫不费力——无需本地启动。只需指向 WS 端点并自动执行。

像专业人士一样浏览 DOM:get_children_elements() 和 get_siblings_elements()

两个令人愉快的帮手,可以有目的地穿越复杂的布局:

1
2
3
4
5
6
7
8
9
10
11
12
# Grab direct children of a container
container = await tab.find(id='cards')
cards = await container.get_children_elements(max_depth=1)
 
# Want to go deeper? This will return children of children (and so on)
elements = await container.get_children_elements(max_depth=2)
 
# Walk horizontal lists without re-querying the DOM
active = await tab.find(class_name='item-active')
siblings = await active.get_siblings_elements()
 
print(len(cards), len(siblings))

使用它们来切割样板、表达意图并保持抓取/自动化逻辑干净易读——尤其是在***格、列表和菜单中。

WebElement:状态等待和新的公共 API

  • wait_until(...) on WebElement 要用最少的代码等待元素状态:
1
2
3
4
5
# Wait until it becomes visible OR the timeout expires
await element.wait_until(is_visible=True, timeout=5)
 
# Wait until it becomes interactable (visible, on top, receiving pointer events)
await element.wait_until(is_interactable=True, timeout=10)
  • 方法现已公开 WebElement:
    • is_visible()
      • Checks that the element has a visible area (> 0), isn’t hidden by CSS and is in the viewport (after scroll_into_view() 需要时)。交互前有用的预检查。
    • is_interactable()
      • “点击就绪”状态:结合可见性、启用度和指针事件命中测试。非常适合避免丢失点击的稳健流程。
    • is_on_top()
      • 验证元素是否是预期点击点的顶部命中测试目标,避免叠加。
    • execute_script(script: str, return_by_value: bool = False)
      • 在元素自己的上下文中执行 JavaScript(其中 this 是元素)。非常适合微调和快速检查。
1
2
3
4
5
6
7
# Visually outline the element via JS
await element.execute_script("this.style.outline='2px solid #22d3ee'")
 
# Confirm states
visible = await element.is_visible()
interactable = await element.is_interactable()
on_top = await element.is_on_top()

这些新增功能简化了单击/键入之前的等待和状态验证,减少了不稳定并使自动化更具可预测性。

📦 安装

1
pip install pydoll-python

就是这样!只需安装并开始自动化。

🚀 开始

您的第一个自动化

让我们从一个真实的例子开始:执行 Google 搜索并点击第一个结果的自动化。通过此示例,我们可以看到库的工作原理以及如何开始自动化任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 search_box.press_keyboard_key(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 python'))

无需配置,只需一个简单的脚本,我们就可以进行完整的谷歌搜索!
好的,现在让我们看看如何使用相同的上一个示例从页面中提取数据。
让我们在下面的代码中考虑一下我们已经在 Pydoll 页面上。我们想要提取以下信息:

  • 项目描述
  • 星数
  • 叉子数量
  • 问题数
  • 拉取请求数

让我们开始吧!为了获取项目描述,我们将使用 xpath 查询。您可以查看有关如何构建自己的查询的文档。

1
2
3
4
description = await (await tab.query(
    '//h2[contains(text(), "About")]/following-sibling::p',
    timeout=10,
)).text

就是这样!让我们了解一下这个查询的作用:

  1. //h2[contains(text(), "About")] - 选择第一个

    包含“关于”
  2. /following-sibling::p - Selects the first

现在让我们获取其余数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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)

我们设法提取了所有必要的数据!

自定义配置

有时我们需要对浏览器进行更多控制。Pydoll 提供了一种灵活的方法来做到这一点。让我们看看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pydoll.browser import Chrome
from pydoll.browser.options import ChromiumOptions as Options
 
async def custom_automation():
    # Configure browser options
    options = Options()
    options.add_argument('--proxy-server=username:password@ip:port')
    options.add_argument('--window-size=1920,1080')
    options.binary_location = '/path/to/your/browser'
    options.start_timeout = 20
 
    async with Chrome(options=options) as browser:
        tab = await browser.start()
        # Your automation code here
        await tab.go_to('https://example.com')
        # The browser is now using your custom settings
 
asyncio.run(custom_automation())

在此示例中,我们将浏览器配置为使用代理和 1920x1080 窗口,以及 Chrome 二进制文件的自定义路径,以防您的安装位置与常见默认值不同。

⚡ 高级功能

Pydoll 提供一系列高级功能,即使是最能满足
要求苛刻的用户。

高级元素搜索

我们有多种方法可以在页面上查找元素。无论您喜欢如何,我们都有一种对您有意义的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import asyncio
from pydoll.browser import Chrome
 
async def element_finding_examples():
    async with Chrome() as browser:
        tab = await browser.start()
        await tab.go_to('https://example.com')
 
        # Find by attributes (most intuitive)
        submit_btn = await tab.find(
            tag_name='button',
            class_name='btn-primary',
            text='Submit'
        )
        # Find by ID
        username_field = await tab.find(id='username')
        # Find multiple elements
        all_links = await tab.find(tag_name='a', find_all=True)
        # CSS selectors and XPath
        nav_menu = await tab.query('nav.main-menu')
        specific_item = await tab.query('//div[@data-testid="item-123"]')
        # With timeout and error handling
        delayed_element = await tab.find(
            class_name='dynamic-content',
            timeout=10,
            raise_exc=False  # Returns None if not found
        )
        # Advanced: Custom attributes
        custom_element = await tab.find(
            data_testid='submit-button',
            aria_label='Submit form'
        )
 
asyncio.run(element_finding_examples())

find 方法更人性化。我们可以按常见属性(如 id、tag_name、class_name 等)进行搜索,直至自定义属性(例如data-testid).

如果这还不够,我们可以使用 query 使用 CSS 选择器、XPath 查询等搜索元素的方法。Pydoll 会自动识别我们正在使用的查询类型。

浏览器上下文 HTTP 请求 - 混合自动化的游戏规则改变者!

您是否曾经希望发出自动继承浏览器所有会话状态的 HTTP 请求? 现在你可以了!

The tab.request 物业给你一个美丽的requests-like 接口,直接在浏览器的 JavaScript 上下文中执行 HTTP 调用。这意味着每个请求都会自动获取 cookie、身份验证标头、CORS 策略和会话状态,就像浏览器自己发出请求一样。

混合自动化的完美选择:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# Navigate to a site and login normally with 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()
 
# Now make API calls that inherit the logged-in session!
response = await tab.request.get('https://example.com/api/user/profile')
user_data = response.json()
 
# POST data while staying authenticated
response = await tab.request.post(
    json={'theme': 'dark', 'notifications': True}
)
 
# Access response content in different formats
raw_data = response.content
text_data = response.text
json_data = response.json()
 
# Check cookies that were set
for cookie in response.cookies:
    print(f"Cookie: {cookie['name']} = {cookie['value']}")
 
# Add custom headers to your requests
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)

为什么这很棒:

  • 不再需要杂耍会话 - 请求自动继承浏览器 cookie
  • CORS 可以正常工作 - 请求遵守浏览器安全策略
  • 非常适合现代水疗中心 - 将 UI 自动化与 API 调用无缝混合
  • 身份验证变得简单 - 通过 UI 登录一次,然后锤击 API
  • 混合工作流 - 为每个步骤使用最佳工具(UI 或 API)

这为需要浏览器交互和 API 效率的自动化场景开辟了令人难以置信的可能性!

新的 expect_download() 上下文管理器 — 强大的文件下载变得简单!

厌倦了与不稳定的下载流程、丢失的文件或活泼的事件***作斗争?遇 tab.expect_download(),一种令人愉快、可靠的文件下载处理方式。

  • 自动设置浏览器的下载行为
  • 适用于您自己的目录或临时文件夹(自动清理!
  • 等待超时完成(这样您的测试就不会挂起)
  • 为您提供一个方便的句柄来读取字节/base64 或检查 file_path

有效的小例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 download:
            # Trigger the download in the page (button/link/etc.)
            await (await tab.find(text='Download latest report')).click()
            # Wait until finished and read the content
            data = await download.read_bytes()
            print(f"Downloaded {len(data)} bytes to: {download.file_path}")
 
asyncio.run(download_report())

想要零麻烦的清理吗?省略 keep_file_at 我们将创建一个临时文件夹,并在上下文退出后自动将其删除。非常适合测试。

具有自定义首选项的全面浏览器控制!(感谢 @LucasAlvws)

想要完全自定义 Chrome 的行为方式? 现在你可以控制一切了!

新的 browser_preferences 系统使您可以访问数百个以前无法通过编程方式更改的内部 Chrome 设置。我们谈论的是远远超出命令行标志的深度浏览器定制!

可能性是无限的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
options = ChromiumOptions()
 
# Create the perfect automation environment
options.browser_preferences = {
    'download': {
        'default_directory': '/tmp/downloads',
        'prompt_for_download': False,
        'directory_upgrade': True,
        'extensions_to_open': ''  # Don't auto-open any downloads
    },
    'profile': {
        'default_content_setting_values': {
            'notifications': 2,        # Block all notifications
            'geolocation': 2,         # Block location requests
            'media_stream_camera': 2, # Block camera access
            'media_stream_mic': 2,    # Block microphone access
            'popups': 1               # Allow popups (useful for automation)
        },
        'password_manager_enabled': False# Disable password prompts
        'exit_type': 'Normal'              # Always exit cleanly
    },
    'intl': {
        'accept_languages': 'en-US,en',
        'charset_default': 'UTF-8'
    },
    'browser': {
        'check_default_browser': False,    # Don't ask about default browser
        'show_update_promotion_infobar': False
    }
}
 
# Or use the convenient helper methods
options.set_default_download_directory('/tmp/downloads')
options.set_accept_languages('en-US,en,pt-BR'
options.prompt_for_download = False

真实世界的电源示例:

  • 静默下载 - 没有提示,没有对话框,只有自动下载
  • 阻止所有干扰 - 通知、弹出窗口、相机请求,凡是你能想到的
  • 非常适合 CI/CD - 禁用更新检查、默认浏览器提示、崩溃报告
  • 多区域测试 - 立即更改语言、时区和区域设置
  • 安全强化 - 锁定权限并禁用不必要的功能
  • 先进的指纹控制 - 修改浏览器安装日期、参与历史记录和行为模式

用于隐身自动化的指纹定制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import time
 
# Simulate a browser that's been around for months
fake_engagement_time = int(time.time()) - (7 * 24 * 60 * 60# 7 days ago
 
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# Restore last session
        'startup_urls': ['https://www.google.com']
    }
}

这种级别的控制以前仅适用于 Chrome 扩展程序开发人员 - 现在它在您的自动化工具包中!

检查一下 documentation 了解更多详情。

并发自动化

Pydoll 的一大优势是由于其异步实现,能够同时处理多个任务。我们可以自动化多个选项卡
同时!让我们看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import asyncio
from pydoll.browser import Chrome
 
async def scrape_page(url, tab):
    await tab.go_to(url)
    title = await tab.execute_script('return document.title')
    links = await tab.find(tag_name='a', find_all=True)
    return {
        'url': url,
        'title': title,
        'link_count': len(links)
    }
 
async def concurrent_scraping():
    browser = Chrome()
    tab_google = await browser.start()
    tab_duckduckgo = await browser.new_tab()
    tasks = [
        scrape_page('https://google.com/', tab_google),
        scrape_page('https://duckduckgo.com/', tab_duckduckgo)
    ]
    results = await asyncio.gather(*tasks)
    print(results)
    await browser.stop()
 
asyncio.run(concurrent_scraping())

我们设法同时从两个页面中提取数据!

还有很多很多!用于反应式自动化、请求拦截和修改等的事件系统。看看文档,你不会
后悔吧!

🔧 快速故障排除

找不到浏览器?

1
2
3
4
5
6
from pydoll.browser import Chrome
from pydoll.browser.options import ChromiumOptions
 
options = ChromiumOptions()
options.binary_location = '/path/to/your/chrome'
browser = Chrome(options=options)

浏览器在 FailedToStartBrowser 错误后启动?

1
2
3
4
5
6
7
from pydoll.browser import Chrome
from pydoll.browser.options import ChromiumOptions
 
options = ChromiumOptions()
options.start_timeout = 20  # default is 10 seconds
 
browser = Chrome(options=options)

需要代理吗?

1
options.add_argument('--proxy-server=your-proxy:port')

在 Docker 中运行?

1
2
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')

📚 文档

如需完整的文档、详细示例和对所有 Pydoll 功能的深入研究,请访问我们的 official documentation.

文档包括:

  • 入门指南 - 分步教程
  • API 参考 - 完整的方法文档
  • 先进技术 - 网络拦截、事件处理、性能优化

The chinese version of this README is here.

🤝 贡献

我们希望您的帮助让 Pydoll 变得更好!查看我们的 contribution guidelines 开始。无论是修复错误、添加功能还是改进文档 - 欢迎所有贡献!

请确保:

  • 编写新功能或错误修复的测试
  • 遵循代码样式和约定
  • 对拉取请求使用常规提交
  • 在提交之前运行 lint 检查和测试

💖 支持我的工作

如果您发现 Pydoll 有用,请考虑 supporting me on GitHub.
您将获得独家福利,例如优先支持、自定义功能等等!

现在不能赞助?没问题,你仍然可以通过以下方式提供很多帮助:

  • 为存储库加星标
  • 在社交媒体上分享
  • 撰写帖子或教程
  • 提供反馈或报告问题

每一点支持都会产生影响/

💬 传播信息

如果 Pydoll 节省了您的时间、心理健康或键盘免于被砸碎,请给它一个 ⭐ ,分享它,或者告诉你奇怪的开发朋友。

📄 许可证

Pydoll 在 MIT License.

Pydoll — 让浏览器自动化变得神奇!

免责声明 © 2025 - 虚宝阁

本站部分源码来源于网络,版权归属原开发者,用户仅获得使用权。依据《计算机软件保护条例》第十六条,禁止:

  • 逆向工程破解技术保护措施
  • 未经许可的分发行为
  • 去除源码中的原始版权标识

※ 本站源码仅用于学习和研究,禁止用于商业用途。如有侵权, 请及时联系我们进行处理。

侵权举报请提供: 侵权页面URL | 权属证明模板

响应时效:收到完整材料后48小时内处理

相关推荐