简介:DrissionPage 是面向开发者、测试人员与运维工程师的 Web 自动化集成工具,提供脚本录制、元素定位、数据处理、多线程执行及报告生成等核心能力,可广泛应用于自动化测试、数据抓取与持续集成场景。这份 v4.0.2 资源包共含 79 个文件,核心为 39 个 Python 源码与 31 个类型标注文件,配合 README、LICENSE、配置说明及示例页面,构成一套结构完整的工具项目,方便直接阅读源码或集成到自己的流程中。压缩包整体仅 172KB,轻量易部署,适合前端、爬虫方向的学生借助源码开展毕业设计或论文研究,理解浏览器自动化底层实现。目前已有 572 人学习下载,对于希望快速掌握 DrissionPage 用法或二次开发个性化自动化脚本的读者来说,是一份值得收藏的参考资料。
1. 拿到 DrissionPage v4.0.2 这个压缩包,先理解它到底在解决什么问题
做 Web 自动化的人第一次见到 DrissionPage 这个集成工具,最容易犯的错是拿它跟 Selenium 做简单对比,然后问“又是换了个壳吧”。真跑起来才发现,它把 Chromium 内核和 requests 会话合并到了同一个上下文里,页面里登录过的 cookie、localStorage、UA 全都能被后续请求直接复用,不用再手动搬运登录态。这一下就让页面自动化巡检、后台批量操作这类脚本的写法彻底变了——你不需要在浏览器和 requests 之间来回导数据,这也是标题里“集成”两个字真正的分量。这个压缩包适合四类人:写周期性巡检页面健康度的、处理多账号后台操作与表单提交的、想把旧脚本改成浏览器控件的、以及刚接触 Web 自动化想少踩环境坑的新手。
2. 先搞清楚“同源会话”这个设计,再谈怎么安装和跑通最小示例
2.1 它和 Selenium 的本质区别:浏览器与 requests 共用一套状态
用 Selenium 写页面自动化,你面对的是两层数据:浏览器层面有一套 cookie、localStorage、sessionStorage,requests 层面又有另一套。页面里登录完,切到 requests 去拿接口数据,你得手动把 cookie 拼进请求头,登录态还会因为浏览器上下文关闭就失效。这已经是行业里最常见的自动化翻车点,也是很多开发回头去骂“这是玄学”的原因——本质只是两层会话没有被统一。
DrissionPage 把这两件事合并到一个会话里。你用浏览器模式打开页面,页面里通过 JS 写入的 cookie、发起请求时生成的身份标识,都会被同一个上下文记录;切到数据模式去请求接口,自动带上当前页面持有的状态。这个设计带来的直接收益是:巡检脚本里“打开页面看状态”和“请求接口拿数据”不再是两次独立动作,而是一次会话里的连续行为。理解了这一点,后面所有参数和代码就比较容易对号入座。
2.2 从 zip 到能跑的最小命令
v4.0.2 是压缩包形式,解压后你通常能看到源码目录、依赖清单和使用说明。我的习惯是先看一遍说明文件,确认依赖版本要求,再决定用离线安装还是直接拉取已发布的主库。这个版本建议在 Python 3.8 以上的环境里跑,低于这个版本容易出现类型语法不兼容。
# 先装主库,压缩包里的源码一般对应同一个发布版本 python -m pip install drissionpage # 验证安装是否真的可用 python -c "from DrissionPage import ChromiumPage; print('ok')"第一行命令把主库装进当前 Python 环境;第二行看起来简单,但值得执行。很多解压后闪退、报 ModuleNotFoundError 的场景,其实都是这步没踩实,环境里残留了旧版本或者装到了别的解释器。如果你手头是离线内网环境,就把压缩包里的依赖目录指给 pip 用,常见做法是python -m pip install --no-index --find-links=./libs drissionpage,这样不会去外网拉包。跑通这步后再谈浏览器控制。
2.3 第一个能跑通的最小示例:启动浏览器并读取标题
from DrissionPage import ChromiumPage # 启动本机已安装的 Chrome 内核 page = ChromiumPage() page.get('https://example.com') print(page.title) page.quit()这段代码做的事就三件:启动浏览器、打开指定页面、打印页面标题。逻辑上ChromiumPage()负责拉起一个受控的 Chrome 实例;get()等于在地址栏输入 URL 回车;page.title拿当前标签页的标题;quit()关闭浏览器,这个释放动作在长跑脚本里尤其重要,否则进程会越积越多。第一行如果报错说找不到浏览器,最常见原因是本机 Chrome 版本过旧或安装路径不在默认位置,后面避坑章节细说。跑通这个最小示例,说明压缩包的环境依赖和内核驱动是匹配的,你才刚跨过门槛。
3. 四个控制器怎么选:ChromiumPage、SessionPage、WebPage、ChromiumOptions
3.1 控制器不是越多越好,是按任务形态分情况选
接触 DrissionPage 时最容易晕的是控制器太多:ChromiumPage、SessionPage、WebPage、ChromiumOptions,名字长得也像。组合起来无非是一张表的事。我一般把问题反过来问自己:这轮任务需不需要看一眼真实渲染后的页面?
| 控制器 | 模式 | 典型场景 |
|---|---|---|
| ChromiumPage | 浏览器渲染模式 | 页面点击、表单填写、等待 JS 渲染后取数据 |
| SessionPage | requests 会话模式 | 纯接口调用、JSON 拉取、文件下载 |
| WebPage | 双模式切换 | 同一会话里先页面操作再请求接口 |
| ChromiumOptions | 启动参数配置 | 设置无头模式、下载路径、UA、用户目录 |
如果你只是巡检一个接口是否返回 200,SessionPage 足够,别的控制器反而多余。如果你想处理一个要点击按钮后经过 JS 渲染的后台表格,则必须用 ChromiumPage 或 WebPage。这里有一个容易忽略的代价:浏览器渲染模式比纯 requests 会话慢一到两个数量级,因为它要启动完整的内核、加载页面资源、执行脚本。巡检大批量 URL 时,能走 SessionPage 的不要强行开浏览器。
3.2 SessionPage 的极简请求与返回对象
from DrissionPage import SessionPage # 纯会话模式,不需要浏览器进程 s = SessionPage() resp = s.get('https://httpbin.org/json') print(resp.status_code) print(resp.json)这段代码里SessionPage()创建了一个类似 requests.Session 的会话对象,但底层会和浏览器模式共享状态配置;s.get()返回响应对象,.status_code是状态码,.json是解析后的 JSON 数据。参数层面,get()支持timeout、headers、params,接口巡检脚本里我会习惯写明 timeout,比如s.get(url, timeout=15),不写的话可能用默认值挂很久。SessionPage 的价值不在于它比 requests 快,而在于它能复用你给浏览器模式配置的同一套身份状态,又不额外消耗渲染资源。
3.3 WebPage:一个容器里先看页面再拿接口数据
from DrissionPage import WebPage # 默认以 d_mode(浏览器渲染模式)启动 page = WebPage() page.get('https://example.com') print(page.title) # 切换到 s_mode,沿用当前页面的请求上下文 page.change_mode() resp = page.get('https://httpbin.org/get') print(resp.status_code)WebPage()同时持有渲染模式和数据模式,初始在 d_mode;change_mode()负责切换,切换后get()的语义跟着变——在 d_mode 里打开的是可视化页面,在 s_mode 里发出的是纯请求。参数上没有额外花哨的东西,关键是记住一个边界:s_mode 没法执行页面里的 JS,也拿不到动态渲染后的 DOM,需要这两样就得先切回 d_mode。同一个容器里先页面登录、再切数据模式调接口,适合做多账号后台操作,你不需要再把 cookie 导进导出,这是它对比 Selenium 加 requests 组合最省事的地方。
4. 配置 ChromiumOptions 时,先抓住三个直接影响成败的参数
4.1 下载目录与失败重试是两个必调参数
默认情况下浏览器会把下载文件丢进系统下载目录,脚本跑久了文件散落得到处都是。常见的做法是用 ChromiumOptions 指定下载路径,同时调大失败重试阈值。下面的代码演示典型配置:
from DrissionPage import ChromiumOptions, ChromiumPage co = ChromiumOptions() co.set_download_path('D:/audit_downloads') # 所有下载落盘到这个目录 co.retry_times(3) # 连接失败时重试 3 次 co.retry_interval(1.5) # 每次重试间隔 1.5 秒 page = ChromiumPage(co) page.get('https://example.com/file.zip') page.wait_download_begin() page.wait_download_ok()set_download_path接收一个绝对路径,路径不存在时工具会尝试创建;retry_times和retry_interval控制网络异常时的重试策略。参数不宜调得过大,重试 3 次、间隔 1.5 秒对大多数巡检场景已经够用。设置过大的重试次数会让脚本在一个坏目标上卡很久,反而影响整体巡检周期。wait_download_begin和wait_download_ok是等待下载开始的典型调用,实际下载完成后你可以继续用文件系统相关逻辑去校验文件大小。
4.2 多账号与请求头配置:不让每次启动都暴露同一张脸
import random ua_list = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Chrome/121.0.0.0', ] co = ChromiumOptions() co.set_user_agent(random.choice(ua_list)) co.set_headless(True) co.set_user_data_dir(f'C:/profile/user_{random.randint(1, 99)}')set_user_agent设置请求头里的 UA,每次换一个能减少被识别成固定脚本特征的概率;set_headless(True)开启无头模式,后台运行不弹窗,适合服务器上长跑;set_user_data_dir指定用户数据目录,每个账号独立目录可避免登录态互相覆盖。参数上有几个细节值得注意:多账号场景不要几个账号共享同一个 user_data_dir,否则后登录的账号会把前面顶掉;无头模式在部分需要物理显卡渲染的页面上会出现白屏,这类页面反而要切回有头模式排查,这个在后面排查章节会再提。
4.3 接管本机浏览器要懂 user_data_dir 的冲突边界
很多人以为 DrissionPage 接管 Chrome 就是直接连接当前已打开的浏览器,这是个常见误解。默认情况下它启动的是一个全新的受控实例,和你在桌面手动打开的浏览器是隔离的。想让脚本用上你日常登录过的会话,可以用operating_path()指向本机 Chrome 的用户目录,或者手动启动 Chrome 时带上调试端口再让 DrissionPage 接管。但这里有坑:如果你已经开着一个 Chrome 窗口占用了默认用户目录,再次用同一个 user_data_dir 启动受控实例,会直接启动失败或者打开到相同进程里。我处理多账号时会为每个账号复制一份独立目录,脚本启动前先确认没有残留的 chrome 进程占用端口,这是最省心的姿势。
5. 避坑排查:DrissionPage 集成工具最常见的五个现场事故
5.1 解压 zip 后运行入口程序闪退
现象:从压缩包解压后,运行 demo 脚本或入口程序,窗口一闪就消失,控制台看不到任何报错。原因通常是 Python 环境版本不对,或者依赖包没有完整安装到当前解释器路径下。v4.0.2 要求 Python 3.8 以上,低于这个版本在语法解析阶段就会异常退出,而有些 windows 环境里系统自带的是 Python 2 或 3.6,命令python指向的却不是你先装的版本。
解决:在命令行用python --version先确认解释器版本;再执行python -m pip list查看 DrissionPage 相关包是否出现在列表。若依赖没装全,走python -m pip install -r requirements.txt补装。注意:如果压缩包里有多个 demo 脚本,先用最小版跑通,不要直接跑带图形界面的大脚本,这样能把环境问题和业务逻辑问题隔离开。
5.2 页面元素定位到了但始终点不动
现象:元素用page.ele()找得到,打印出来有文本有属性,但执行click()没反应,甚至报not clickable。原因有两种高频场景:一是元素被 iframe 包着,DOM 在子框架里,当前作用域并不在目标文档上;二是元素本身被遮挡,比如悬浮层或弹窗压在上面。
解决:先page.into_iframe()切进对应 iframe,操作完再page.exit_iframes()退出来;如果是遮挡,用ele.wait_clickable()先等它可点击,再不行用page.scroll_to_see(ele)把它滚动进可视区再点。我的经验是:出现“找得到但点不动”,优先检查 iframe,再检查遮挡,最后才怀疑元素坐标。把这三步做成一个固定排查顺序,能省下很多瞎试的时间。
5.3 请求 403 或验证码频繁弹出
现象:同一套脚本在开发机上跑正常,部署到服务器跑就频繁 403,或者弹出验证码。原因大概率是目标站点识别到了无头浏览器的特征。无头模式里 WebDriver 相关标记、屏幕尺寸、UA 这几个维度和真实浏览器有差异,服务端风控很容易识别,这不属于脚本语法问题。
解决:关闭无头模式,改用有头模式跑一轮看是否恢复;同时通过 ChromiumOptions 显式设置 UA、窗口大小、--disable-blink-features=AutomationControlled之类参数,降低自动化特征。另一个有效的做法是降低请求频率,重试间隔调到 2 秒以上。验证码弹窗一旦出现,先停脚本手工处理一次,别立刻堆参数,否则容易越调越乱。
5.4 浏览器登录了,但 requests 会话拿不到相同登录态
现象:用页面模式成功登录,切到 SessionPage 或 requests 侧发请求却提示未认证。原因十有八九是控制器选错了。SessionPage 是独立会话,它并不会自动附加你 ChromiumPage 页面里产生的 cookie,只有 WebPage 这种双模式容器才保证状态在一个上下文里。这个误用几乎人人都会踩一次。
解决:如果你的任务需要“页面登录 + 接口验证”,直接用WebPage替代两处控制器,在 d_mode 登录后change_mode()再请求接口。如果你坚持用 SessionPage,那就手动把 cookie 从页面会话里取出,通过set_headers或SessionPage对应的 cookie 设置方法塞进去,但这会回归到老式的搬运逻辑,显然更麻烦。首选还是换 WebPage。
5.5 脚本打包后到别的机器运行报 DLL 错误
现象:本地跑得好好的,用 PyInstaller 打成 exe 后搬到另一台机器,启动时报找不到 DLL 或者浏览器内核路径无效。原因有两个叠在一起:一是打包时没有把 DrissionPage 依赖的本地动态库一起收录;二是目标机器没有安装 Chrome,或者 Chrome 版本过旧,受控内核启动不了。
解决:打包命令里显式 include 相关依赖,例如用--hidden-import把浏览器控制相关的模块带进去;同时启动脚本里加一段浏览器路径检查逻辑,发现默认路径不存在就提示安装 Chrome 或让用户通过driver_path指定内核路径。我一般会给目标机器准备一份可用的浏览器安装包,部署文档里写明版本要求,这能把环境冲突拦在运行之前。
6. 把工具落成一个周期性巡检脚本:完整模板与验证土办法
6.1 巡检脚本的骨架:循环、超时、日志与异常兜底
很多团队做“web页面自动化巡检”时会把代码写重:又是数据库又是可视化面板,反而忽略了最基本的循环保障。先看一段可以直接改着用的模板:
import time from DrissionPage import ChromiumPage urls = [ 'https://example.com/login', 'https://example.com/dashboard', ] base_timeout = 15 # 单页超时秒数 wait_interval = 300 # 两轮巡检间隔秒数 page = ChromiumPage() for round_no in range(10): # 先跑 10 轮观察稳定性 for url in urls: try: page.get(url, timeout=base_timeout) title = page.title if title == '': raise ValueError('页面标题为空,疑似拦截页') print(f'[ok] {url} title={title}') except Exception as e: # 异常不进黑匣子,直接落日志 with open('audit.log', 'a', encoding='utf-8') as f: f.write(f'{time.strftime("%Y-%m-%d %H:%M:%S")} {url} {e}\n') time.sleep(wait_interval) page.quit()几个参数值得按现场情况改:base_timeout建议 10 到 20 秒,太短会误报,太长会让一轮巡检卡死;wait_interval是两轮之间的间隔,巡检内网页面 60 秒足够,巡检公网页面建议 300 秒以上,避免被目标服务端判定为高频访问。page实例只创建一次,而不是在循环里反复创建,这是巡检脚本最容易忽略的性能点。异常信息统一走文件日志,不加告警的话至少要能看到历史失败记录。
6.2 验证链路成立的两个土办法
脚本写完先别急着挂后台,用两个土办法验证。第一招:故意把其中一个 URL 改成一个不存在的路径,比如在目标后加/__test__,跑完一轮确认日志落了记录,说明异常捕获和日志链路是真的生效,而不是表面跑得好看。第二招:选一个已知稳定的页面,先手动访问五次记录平均加载耗时,再让脚本跑五轮,如果脚本页面加载耗时比手动高出一倍以上,优先怀疑代理或网络层,而不是怀疑工具本身。这两个办法能在五分钟内定位八成“脚本看着在跑但实际没意义”的问题。
6.3 收尾习惯:一个跑过几百次周期的经验
我最后留一个自己的习惯:每次升级 DrissionPage 版本,不要直接线上替换,先拿一条固定 URL 做回放测试。这个工具更新后,个别参数名和默认行为会变,尤其是重试策略和超时处理,升级后脚本可能不报错但行为已经不同。回放测试就是拿上一版本跑正常的用例再执行一遍,对比标题、耗时、日志记录三项有没有变化。有了这个习惯,你可以在不读 changelog 的情况下也及时发现回归,减少生产期翻车。另一个习惯是不要开无头模式追查页面渲染问题,无头模式节省的不过是资源,但页面卡在白屏、元素不可见这类问题,换有头模式一看就明,省下的排查时间远超多开的窗口成本。这套工具用顺之后,你会发现自己对“页面自动化”的看法变了,它不是用来替代手工点击的玩具,而是能把巡检、监控、批量后台操作统统收进一条时间线里的正经方案。希望帮到你。
本文还有配套的精品资源,点击获取