- 网页爬虫
- 浏览器控制
【免费下载链接】DrissionPage
Python based web automation tool. Powerful and elegant.
导读
DrissionPage 是当前仓库 DrissionPage 所承载的一款基于 Python 的网页自动化工具,其核心主张是"简洁而强大"。本文以仓库 README 为主线,结合 Chromium 内核实现、Session 页面封装 与 定位语法解析器 等源码,系统讲解它的运行环境、自研内核对比 Selenium 的优势、内置亮点功能、ini 配置体系与 POM 使用模式。读完本文,你将掌握 DrissionPage 的架构定位、核心 API 用法以及如何在真实项目中落地这套自动化方案。
运行环境与支持范围
根据 README「运行环境」一节的说明,DrissionPage 对运行环境的要求非常宽松:
- 操作系统:Windows、Linux、Mac 全平台支持;
- Python 版本:3.6 及以上;
- 浏览器:支持 Chromium 内核浏览器(如 Chrome 和 Edge)以及 electron 应用。
这一节的实际含义是:DrissionPage 的浏览器控制能力建立在 Chromium 的 DevTools 协议(CDP)之上,因此凡是能暴露调试端口的 Chromium 内核浏览器都能接入。仓库入口文件 DrissionPage/init.py 只导出了四个公开类:Chromium、SessionPage、ChromiumOptions、SessionOptions,印证了"一个工具覆盖两种页面操作模式"的设计——Chromium负责真实浏览器(d 模式),SessionPage负责基于 requests 的纯请求模式(s 模式)。
版本方面,当前仓库 version.py 记录版本号为5.0.0b0(5.0 首个测试版),文档内容与该版本行为一致,安装与使用请以当前仓库实际代码为准。
理念:简洁而强大
README 用一句话概括项目理念:简洁而强大("Simpler and more powerful")。这一理念贯穿了源码设计的每个层面:
- 入口极简:只需一行
Chromium()即可启动并接管浏览器(见 chromium.py 的__new__逻辑,它会自动寻找端口、启动进程或接管已存在的浏览器); - 定位极简:内置一套"文本即语法"的定位系统(详见下文"极简定位语法");
- 封装极简:大量常用功能被收敛成
set、wait、states、scroll等挂件(widget),避免在业务代码中堆积样板逻辑,这在 chromium_base.py 的挂件实现中清晰可见。
自研内核:与 Selenium 的对比
README「强大的自研内核」一节明确指出,本库采用全自研内核,不基于 Selenium 的 webdriver,并列出以下对比优点。我们逐一结合源码验证。
1. 不基于 webdriver,无需下载浏览器驱动
Selenium 需要为每个浏览器版本下载对应 driver;DrissionPage 直接通过 CDP WebSocket 与浏览器通信。从 chromium.py 可以看到,连接的核心动作是构造ws://{address}/devtools/browser/{browser_id}地址并创建Driver实例,全程不涉及任何 driver 可执行文件。
2. 运行速度更快
两条技术路径共同带来速度提升:
- 传输层轻量化:基于 WebSocket 的 CDP 直连,省去 driver 进程转发;
- 解析引擎用 lxml:README 在亮点功能中特别强调"内置 lxml 作为解析引擎,解析速度成几个数量级提升"。从 locator.py 可以看到,当定位结果是 CSS 时,代码会尝试用
lxml.cssselect将 CSS 翻译成 XPath 后走 XPath 查询,充分利用 lxml 的解析性能。
3. 跨 iframe 查找元素,无需切入切出
Selenium 操作 iframe 必须执行switch_to.frame()来回切换;DrissionPage 把 iframe 视为普通元素,可以直接在 frame 内继续查找。源码在 chromium_base.py 提供get_frame()/get_frames()方法,并且底层维护了Tabs._frames映射(chromium.py),记录每个 frame 属于哪个 tab,再通过 CDP 的DOM.performSearch全局搜索(includeUserAgentShadowDOM=True)实现跨 frame 查找,逻辑更统一、更清晰。
4. 可同时操作多个标签页,无需切换
浏览器对象Chromium持有标签页管理器Tabs(chromium.py),内部以{session_id: target_id}、{target_id: {session_id}}等映射维护所有标签页会话。每个ChromiumTab都是独立对象(chromium_tab.py),tab.get()、tab.ele()各自独立执行,天然支持多标签页并发操作,无需切换焦点。
5. 直接读取浏览器缓存保存图片,无需 GUI 点击另存
元素截图与资源获取均通过 CDP 完成。页面级截图走Page.captureScreenshot(chromium_base.py),图片等二进制资源可通过脚本从缓存读取,全程无 GUI 交互。
6. 可以对整个网页截图,包括视口外部分
get_screenshot(full_page=True)的实现(chromium_base.py)通过self.rect.size获取整页宽高,再以captureBeyondViewport=True配合clip视口参数调用 CDP 截图,覆盖视口外内容。
7. 可处理非 open 状态的 shadow-root
README 特别提到可处理"非open状态的 shadow-root"。元素对象提供sr属性(chromium_element.py),通过DOM.describeNode获取shadowRoots并包装为ShadowRoot对象,配合DOM.performSearch的includeUserAgentShadowDOM参数,使查找能穿透 closed shadow DOM。
亮点功能逐个拆解
README「亮点功能」一节罗列了自研内核之外的大量人性化设计,下面结合源码逐个展开。
极简的定位语法
这是 DrissionPage 最具辨识度的能力。它把 CSS、XPath、文本、属性等查找方式统一为一种极简字符串语法,由 locator.py 负责解析。常用写法包括:
| 写法 | 含义 | 内部处理 |
|---|---|---|
#id/.class | 按 id / class 查找 | _preprocess展开为@id/@class后走 XPath |
t:div/tag:div | 按标签名查找 | 生成//*[name()="div"] |
text:登录 | 文本模糊匹配 | 生成//*/text()[contains(.,"登录")]/.. |
text=登录 | 文本精确匹配 | 生成//*[text()="登录"] |
text^xxx/text$xxx | 文本以 xxx 开头 / 结尾 | 生成starts-with/substring表达式 |
@class=xxx | 单属性精确查找 | 生成//*[@class="xxx"] |
@@class=a@id=b | 多属性"与"查找 | 多个条件用and连接 |
@\|class=a@id=b | 多属性"或"查找 | 多个条件用or连接 |
@!class | 排除某属性 | 生成not(...)条件 |
xpath://.../css:... | 原生 XPath / CSS | 直接透传 |
ax:role=button@name=确定 | 无障碍树查找 | 解析成role、accessibleName键值 |
从上表可以看到,=:^$四个符号分别表示精确、模糊、开头、结尾匹配,:^$三种前缀都可用在文本、属性和标签上。解析逻辑在str_to_xpath_loc()与_preprocess()(locator.py)中逐分支生成 XPath,多属性@@/@|/@!的"与 / 或 / 非"组合由_make_multi_xpath_str()(locator.py)完成。此外get_loc()还兼容 Selenium 风格的('id', 'value')元组定位(translate_loc()),迁移旧代码成本很低。
定位入口统一为ele(locator, index)与eles(locator):index控制返回第几个(负数表示倒数),返回ChromiumElement或元素列表。BaseElement._ele()(base.py)在找不到元素时返回NoneElement,并可通过全局设置Settings.set_raise_when_ele_not_found(True)改为抛出ElementNotFoundError。
无处不在的等待与自动重试
README 指出"无处不在的等待和自动重试,使不稳定的网络变得易于控制"。在源码中有三层体现:
- 页面加载等待:
ChromiumBase._d_connect()(chromium_base.py)通过Page.navigate发起导航,随后_wait_loaded()轮询_ready_state(complete/interactive)直到加载完成或超时; - 元素查找等待:
wait_for_ele()在超时时间内反复调用DOM.performSearch,直到找到目标元素或超时,这与wait.eles_loaded()、wait.ele_displayed()等显式等待方法一脉相承; - 请求自动重试:
Chromium.get()和SessionPage.get()/post()都接受retry、interval参数。以 session_page.py 的_make_response()为例,默认按retry_times(默认 3 次)循环发起请求,每次失败后sleep(interval)(默认 2 秒)再重试,直到成功或达到上限。
这些默认值来自 configs.ini 的[others]段(retry_times = 3、retry_interval = 2),可用ChromiumOptions.set_retry()修改。
强大的下载工具
README 提到"提供强大的下载工具,操作浏览器时也能享受快捷可靠的下载功能"。浏览器级下载由DownloadManager(downloader.py)实现:它监听 CDP 的Browser.downloadWillBegin与Browser.downloadProgress事件,把下载任务包装成DownloadMission,支持:
- 指定保存路径(
set_path()); - 重命名与强制后缀(
set_rename()、set_suffix()); - 同名文件的处理策略(
set_file_exists():rename/overwrite/skip); - 任务取消(
cancel())与跳过(skip())。
同时,ChromiumBase._download_by_browser()(chromium_base.py)提供编程式下载:动态创建一个带download属性的<a>元素并触发点击,把下载行为统一交给下载管理器,从而实现"操作浏览器时也能快捷下载"。
反复使用已经打开的浏览器,调试方便
这是开发调试体验的关键设计。Chromium()的__new__(chromium.py)会先探测目标地址(默认127.0.0.1:9222)上是否已有可控制的浏览器:
- 若已存在且端口可连通(
connect_browser()中test_connect()成功),则直接接管该浏览器,不重新启动(browser.py); - 若不存在则按配置启动一个新浏览器实例。
因此调试时可以先用命令行chrome --remote-debugging-port=9222打开浏览器并登录好账号,再运行脚本直接复用会话,无需每次从零启动。existing_only配置项进一步强制"只接管已存在浏览器,否则报错"。
ini 文件保存常用配置,自动调用
README 强调"使用 ini 文件保存常用配置,自动调用,提供便捷的设置,远离繁杂的配置项"。仓库自带默认配置文件 configs.ini,各段作用如下:
| 配置段 | 关键项 | 默认值 | 说明 |
|---|---|---|---|
[paths] | download_path、tmp_path | 空 | 下载目录、临时目录 |
[chromium_options] | address | 127.0.0.1:9222 | 浏览器调试地址 |
browser_path | chrome | 浏览器可执行文件路径 | |
arguments | 一组--启动参数 | 含--no-first-run、--disable-popup-blocking等 | |
extensions | [] | 加载的插件目录列表 | |
prefs | 弹窗、通知相关偏好 | 写入用户目录 Preferences 文件 | |
flags | {} | 实验特性开关 | |
load_mode | normal | 页面加载模式 | |
user | Default | 浏览器用户目录 profile | |
auto_port | False | 是否自动分配端口 | |
system_user_path | False | 是否复制系统浏览器用户目录 | |
existing_only | False | 是否只接管已存在浏览器 | |
new_env | False | 是否使用全新环境 | |
[session_options] | headers | Safari UA 等一组头 | requests 会话默认请求头 |
[timeouts] | base、page_load、script | 10、30、30 | 三种超时(秒) |
[proxies] | http、https | 空 | 代理设置 |
[others] | retry_times、retry_interval | 3、2 | 请求重试次数与间隔 |
ChromiumOptions(read_file=True)构造时会通过OptionsManager自动读取 ini(chromium_options.py),并把arguments中解析出的--user-data-dir、--profile-directory同步为user_data_path与user。所有set_*方法都返回对象自身,支持链式调用;最后用save()/save_to_default()写回 ini,实现配置持久化。
值得展开的是arguments中几个常用启动参数,可通过ChromiumOptions的语义化方法设置:
headless(True)→ 追加--headless=new无头模式;no_imgs()/no_js()/mute()→ 分别追加--blink-settings=imagesEnabled=false、--disable-javascript、--mute-audio;incognito()→ 同时追加--incognito与--inprivate(兼容 Edge);ignore_certificate_errors()→--ignore-certificate-errors;set_user_agent()→--user-agent;set_proxy()→--proxy-server(自动解析代理账号密码);set_load_mode()→ 可选normal、eager、none,对应页面加载完成判定策略(chromium_base.py 中eager模式在 DOM 触发后即可Page.stopLoading)。
这些语义化方法定义在 chromium_options.py,比手写原始参数更不易出错。
内置 lxml 解析引擎
如前所述,CSS 定位会优先尝试用lxml.cssselect翻译为 XPath(locator.py);而s_ele()系列方法(chromium_base.py)会把实时页面 HTML 交给make_session_ele()生成 lxml 元素对象,供快速批量解析。这就是"解析速度成几个数量级提升"的实现基础。
使用 POM 模式封装,可直接用于测试
POM(Page Object Model)是测试领域的主流设计模式。DrissionPage 的公开类本身就是天然 Page Object:
- 每个页面/标签对象(
ChromiumTab、SessionPage)封装了该页面的全部操作; - 元素对象(
ChromiumElement、SessionElement)封装了查找与交互; - 测试代码只需继承或组合这些对象即可组织页面层与用例层,无需重复实现底层协议细节。
同时 settings.py 提供了set_raise_when_ele_not_found、set_raise_when_click_failed、set_raise_when_wait_failed等开关,把"找不到就静默返回 / 抛异常"的行为统一配置,便于在测试与脚本两种场景间切换。
高度集成的便利功能
从源码结构看,便利功能被封装成大量"挂件",按职责挂在页面或元素上(chromium_base.py):
page.wait→ 等待页面、元素状态(waiter.py);page.set→ 修改属性、cookie、UA、代理、下载设置等(setter.py);page.states→ 查询页面/元素状态(states.py);page.actions→ 鼠标键盘动作链(actions.py);page.scroll→ 滚动控制(scroller.py);page.rect→ 尺寸与坐标(rect.py);page.console→ 浏览器控制台监听(console.py);page.screencast→ 录屏功能(screencast.py);page.listen→ 网络与元素事件监听(listener.py);- 元素侧还有
ele.set、ele.states、ele.wait、ele.rect、ele.sr(shadow-root)、ele.pseudo(伪元素)等。
此外页面对象还内置run_js()(执行 JS)、run_cdp()(直接调 CDP 命令)、add_init_js()(新文档注入脚本)、session_storage()/local_storage()(读写本地存储)、handle_alert()(处理弹窗)、get_screenshot()(截图)、save()(保存页面/PDF)、clear_cache()(清理缓存)、back()/forward()/refresh()(导航)等大量开箱即用的方法。
两种页面模式与核心用法
基于源码入口(init.py),DrissionPage 提供两条主线用法,本文给出最小可运行示例。
d 模式:Chromium 对象操作真实浏览器
from DrissionPage import Chromium, ChromiumOptions # 使用默认配置(自动读取 configs.ini),启动或接管浏览器 browser = Chromium() # 或自定义配置后启动 co = ChromiumOptions() co.set_local_port(9333).headless(True).set_user_agent('Mozilla/5.0 ...') browser = Chromium(co) # 获取最新标签页并访问 tab = browser.latest_tab tab.get('https://example.com') # 极简定位 + 交互 search = tab.ele('#search-input') search.clear() search.input('DrissionPage') tab.ele('t:button@text:搜索').click() # 按标签+属性+文本组合定位 # 多标签页操作:无需切换 tab2 = browser.new_tab('https://example.org') print(tab.title, tab2.title) # 直接读取当前标签页 cookie print(tab.cookies())其中browser.get_tab()、browser.get_tabs()可按 id、序号、标题或 URL 获取标签页;browser.close_tabs()可批量关闭(others=True关闭其他);browser.reconnect()可在浏览器意外断线后重连;browser.quit()关闭浏览器并可选择删除临时用户数据目录。
s 模式:SessionPage 对象发起纯请求
from DrissionPage import SessionPage page = SessionPage() # 自动读取 configs.ini 的 session_options page.get('https://example.com') # 支持自动重试与编码识别 print(page.title) # 从 <title> 提取 print(page.html) # 渲染后的文本 data = page.ele('xpath://div[@class="list"]')从源码看,SessionPage基于requests.Session封装(session_page.py),在_make_response()中自动补全Referer、Host等请求头,并用set_charset()(session_page.py)依次从content-type、HTML<meta charset>、apparent_encoding三个来源确定编码,避免中文网页乱码。
双模式协同:cookies 互通
ChromiumTab同时继承ChromiumBase与SessionPage(chromium_tab.py),因此一个标签页对象可调用change_mode('s')切换到 s 模式、change_mode('d')切回 d 模式,并支持cookies_to_session()/cookies_to_browser()双向搬运 cookie(chromium_tab.py)。典型场景是:先用浏览器登录获取 cookie,再把 cookie 同步给 requests 会话去并发抓取,兼顾反爬与速度。
项目结构与扩展建议
README 的目录组织在源码中对应清晰的包结构(项目根目录):
_base/:BasePage、DrissionElement等抽象基类;_browsers/:Chromium浏览器对象与ChromiumContext隔离环境;_configs/:ChromiumOptions、SessionOptions及默认 configs.ini;_elements/:ChromiumElement、SessionElement、NoneElement;_functions/:定位、cookie、工具、文案等底层函数;_pages/:ChromiumBase、ChromiumTab、ChromiumFrame、SessionPage;_units/:actions、waiter、setter、states、listener、downloader等挂件。
对于有扩展需求的团队,建议基于BasePage/DrissionElement派生自己的页面对象,把重复的登录、翻页、解析逻辑收敛为业务 Page 类,配合Settings的异常开关实现可复用的 POM 测试体系。
结语
DrissionPage 通过 CDP 直连 Chromium 内核,用一套自研的极简定位语法与丰富的挂件体系,把浏览器自动化从"驱动配置繁琐、定位语法割裂、iframe 切换痛苦"的传统体验中解放出来。本文所涉及的特性均可回溯到 DrissionPage 包内的对应源码:内核连接看 chromium.py,定位语法看 locator.py,配置体系看 configs.ini 与 chromium_options.py,双模式协同看 chromium_tab.py。如需上手,直接阅读仓库 README.md 并结合上述源码文件实践即可。
- 网页爬虫
- 浏览器控制
【免费下载链接】DrissionPage
Python based web automation tool. Powerful and elegant.
相关推荐
DrissionPage:网页自动化工具的全能解决方案
DrissionPage:网页自动化工具的全能解决方案 在当今快速发展的互联网时代,网页自动化技术已经成为了许多开发者和测试人员不可或缺的工具。Drission
网页爬虫浏览器控制Python网页自动化工具终极指南:DrissionPage快速上手教程
Python网页自动化工具终极指南:DrissionPage快速上手教程 在当今数字化时代,网页自动化工具已成为提升工作效率的必备利器。DrissionPage
网页爬虫浏览器控制测试RPAHTMLReactParser 完全指南:从零开始掌握 HTML 到 React 转换
HTMLReactParser 完全指南:从零开始掌握 HTML 到 React 转换 HTMLReactParser 是一款功能强大的 HTML 到 Reac
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考