Skip to content

核心概念

Pydoll 建立在几个设计决策之上,它们塑造了你编写每个脚本的方式:无 webdriver、异步 API、拟人化交互,以及事件系统。本页在可动手的层面上解释每一项,好让后续的任务指南更容易理解。

无 webdriver

Pydoll 通过 Chrome DevTools Protocol(CDP)直接连接浏览器,这正是你打开检查器时驱动 Chrome DevTools 的同一套协议。中间没有 webdriver 可执行文件,所以没有东西需要下载,也不用去排查“chromedriver 只支持 Chrome 版本 X”这类不匹配的问题。

graph LR
    subgraph P["Pydoll"]
        direction LR
        P1["你的代码"] --> P2["Pydoll"] --> P3["浏览器 (CDP)"]
    end
    subgraph S["Selenium"]
        direction LR
        S1["你的代码"] --> S2["WebDriver 客户端"] --> S3["chromedriver"] --> S4["浏览器"]
    end

当你启动浏览器时,Pydoll 会用一个远程调试端口拉起你已经安装的那个 Chrome,并向它的 CDP 端点打开一个 WebSocket:

import asyncio

from pydoll.browser.chromium import Chrome


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

asyncio.run(main())

你不用管理端口、连接或浏览器进程;start() 会做这些,而 async with 代码块会在你用完后停止浏览器。

browser 和 tab 对象

两个对象覆盖了你大部分的操作。browserChromeEdge)是你启动的进程。由 browser.start() 返回的 tab 才是你实际操作的对象:导航、元素查找、截图,页面上的一切都通过它进行。

async with Chrome() as browser:
    tab = await browser.start()          # 第一个 tab
    await tab.go_to('https://quotes.toscrape.com')

    second = await browser.new_tab()     # 从 browser 打开更多 tab
    await second.go_to('https://books.toscrape.com')

管理多个标签页请看 标签页,隔离会话请看 浏览器上下文

一切皆异步

每个 Pydoll 调用都是一个协程,所以你要在 async def 函数里 await 它,并用 asyncio.run() 启动程序。这不是外挂上去的兼容层;它正是 Pydoll 同时驱动多个标签页和多个浏览器的方式。由于导航和元素等待大部分时间都处于空闲,asyncio.gather 会让它们并发运行,而不是一个接一个:

import asyncio

from pydoll.browser.chromium import Chrome


async def title_of(browser, url):
    tab = await browser.new_tab(url)
    title = await tab.title
    await tab.close()
    return title


async def main():
    urls = [
        'https://quotes.toscrape.com/page/1/',
        'https://quotes.toscrape.com/page/2/',
        'https://quotes.toscrape.com/page/3/',
    ]
    async with Chrome() as browser:
        await browser.start()
        titles = await asyncio.gather(*(title_of(browser, url) for url in urls))
        print(titles)

asyncio.run(main())

三个页面并发加载,所以整体耗时大约等于最慢的单个页面,而不是三者之和。

刚接触异步 Python?

如果对 asyncawaitgather 还不熟悉,请先读 异步 Python 实战。它只讲够用的 asyncio,足以让你从容读完这些指南的其余部分。

拟人化交互

默认情况下,点击会落在元素中心,输入以固定的节奏进行。传入 humanize=True,Pydoll 就会让光标沿一条曲线路径移动后再点击,并以可变的节奏输入,其中偶尔还会出现被纠正的手误:

search = await tab.find(id='search')
await search.type_text('web scraping', humanize=True)
await search.click(humanize=True)

拟人化是逐次交互按需开启的,所以你可以在会盯着行为看的站点上启用它,而在只看重原始速度的地方跳过它。计时模型请看 拟人化交互,完整的输入 API 请看 键盘鼠标

事件驱动

你可以订阅浏览器事件、在它们触发时运行回调,而不必在循环里轮询页面。这正是你捕获网络流量、对导航做出反应,或等待某个特定请求的方式:

import asyncio
from functools import partial

from pydoll.browser.chromium import Chrome
from pydoll.protocol.network.events import NetworkEvent


async def on_request(tab, event):
    url = event['params']['request']['url']
    if '/api/' in url:
        print(f'API call: {url}')


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

        await tab.enable_network_events()
        await tab.on(NetworkEvent.REQUEST_WILL_BE_SENT, partial(on_request, tab))

        await tab.go_to('https://quotes.toscrape.com')
        await asyncio.sleep(2)

asyncio.run(main())

只启用你用到的事件域,用完就把它们关掉。完整的模型请看 事件,流量捕获请看 网络监控

适用于各种 Chromium 浏览器

同一套 API 可以驱动任何 Chromium 浏览器。Chrome 是首要目标;Edge 有完整支持;其他 Chromium 构建则通过把 binary_location 指向它们来使用。

from pydoll.browser.chromium import Chrome, Edge
from pydoll.browser.options import ChromiumOptions

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

# Edge
async with Edge() as browser:
    tab = await browser.start()

# 任何其他 Chromium 构建(Brave、Vivaldi、Opera ……)
options = ChromiumOptions()
options.binary_location = '/path/to/brave-browser'
async with Chrome(options=options) as browser:
    tab = await browser.start()

下一步