DrissionPage 完全指南

相关文档:Selenium完全指南(/selenium-wan-quan-zhi-nan/) Scrapy分布式采集(/scrapy-fen-bu-shi-cai-ji-wan-quan-zhi-nan/) httpx完全指南(/httpx-wan-quan-zhi-nan/) DrissionPage 是国产的新一代网页自动化工具,融合了浏览器控制和请求发送两种模式: 核心优势: DrissionPage 提供统一的定位语法,比 Selenium 的 find_element(By.XXX) 更简洁。 WebPage 是 DrissionPage 最强

分享

官方文档:https://drissionpage.cn/
适用版本:DrissionPage 4.x(2026-05-07 核实)

相关文档:Selenium完全指南 Scrapy分布式采集 httpx完全指南


1. 基础概念

DrissionPage 是什么

DrissionPage 是国产的新一代网页自动化工具,融合了浏览器控制和请求发送两种模式:

模式 说明
ChromiumPage 控制 Chromium 浏览器(类 Selenium,但更稳定、API 更简洁)
WebPage(d 模式) 浏览器控制模式(同 ChromiumPage)
WebPage(s 模式) 纯 requests 模式(复用浏览器 cookies)
SessionPage 纯 requests 封装,无浏览器

核心优势:

  • d/s 模式一键切换:浏览器模式登录获取 cookie,切到 requests 模式高速采集
  • 元素定位更简洁:支持语义化选择器,比 XPath/CSS 更易读
  • 无需 WebDriver:直接通过 CDP 协议控制 Chrome,更稳定
  • 内置等待机制:自动等待元素出现,无需手动 sleep

安装

pip install DrissionPage

# 首次使用自动下载 Chromium(或指定已有 Chrome 路径)

2. ChromiumPage — 浏览器控制

基础启动

from DrissionPage import ChromiumPage, ChromiumOptions

# 直接启动(自动管理浏览器)
page = ChromiumPage()
page.get("https://example.com")
page.quit()

# 自定义配置
options = ChromiumOptions()
options.headless(False)               # 显示浏览器
options.set_argument("--no-sandbox")
options.set_user_data_path("/tmp/chrome_profile")  # 使用已有 profile(含登录状态)

page = ChromiumPage(options)

ChromiumOptions 常用参数

方法 说明
headless(True/False) 无头模式
no_imgs(True) 不加载图片(加速)
mute(True) 静音
set_argument(arg) 添加启动参数
set_user_data_path(path) 指定用户数据目录
set_proxy(proxy) 设置代理
set_local_port(port) 指定调试端口
auto_port(True) 自动分配端口(多实例时必用)

3. 元素定位

DrissionPage 提供统一的定位语法,比 Selenium 的 find_element(By.XXX) 更简洁。

定位方法

page.get("https://example.com")

# 通过 CSS 选择器(默认,以 # 开头为 id,以 . 开头为 class)
ele = page.ele("#username")
ele = page.ele(".btn-primary")
ele = page.ele("input[name='email']")

# 通过文本内容(精确匹配)
ele = page.ele("登录")

# 通过文本包含
ele = page.ele("text:登录")
ele = page.ele("text^:登录")  # 以"登录"开头

# 通过 XPath
ele = page.ele("xpath://div[@class='content']")

# 通过属性
ele = page.ele("@name=username")
ele = page.ele("@type=submit")

# 通过 tag + 属性
ele = page.ele("tag:input@name=email")

# 查找多个元素
eles = page.eles("tag:li")
eles = page.eles(".item")

相对定位

# 父元素
parent = ele.parent()
parent = ele.parent(2)       # 上两级父元素
parent = ele.parent("div")   # 向上找第一个 div 祖先

# 子元素
child = ele.child()          # 第一个子元素
child = ele.child(2)         # 第二个子元素
child = ele.child("span")    # 第一个 span 子元素
children = ele.children()    # 所有子元素

# 兄弟元素
next_ele = ele.next()
prev_ele = ele.prev()

4. 元素操作

# 点击
ele.click()
ele.click(by_js=True)      # JS 点击(绕过遮罩层)

# 输入文本
ele.input("hello world")
ele.clear()                # 清空
ele.input("hello", clear=True)  # 清空后输入

# 获取属性和文本
text = ele.text            # 文本内容(去除 HTML 标签)
html = ele.inner_html      # innerHTML
attr = ele.attr("href")    # 获取属性值
src = ele.attr("src")

# 获取元素状态
ele.is_displayed           # 是否可见
ele.is_enabled             # 是否可用
ele.is_checked             # 复选框是否选中

# 下拉框
ele.select.by_text("选项文本")
ele.select.by_value("value")
ele.select.by_index(1)

5. 页面操作

# 导航
page.get("https://example.com")
page.back()
page.forward()
page.refresh()

# 等待
page.wait.load_start()     # 等待页面开始加载
page.wait.doc_loaded()     # 等待文档加载完成
page.wait.ele_displayed("#result")   # 等待元素出现
page.wait.ele_deleted("#loading")    # 等待元素消失
page.wait(2)               # 等待 2 秒

# 滚动
page.scroll.to_bottom()    # 滚动到底部
page.scroll.to_top()
page.scroll.down(300)      # 向下滚动 300px
ele.scroll.to_see()        # 滚动直到元素可见

# JS 执行
result = page.run_js("return document.title")
page.run_js("window.scrollTo(0, document.body.scrollHeight)")

# 截图
page.get_screenshot(path="screenshot.png")
ele.get_screenshot(path="element.png")

# Cookie
cookies = page.cookies()
page.set.cookies({"name": "value"})

# 标签页管理
new_tab = page.new_tab("https://example.com")
page.close()
tabs = page.tab_ids

6. WebPage — d/s 模式切换(核心功能)

WebPage 是 DrissionPage 最强大的特性:浏览器模式获取登录态,无缝切换到请求模式高速采集。

from DrissionPage import WebPage

page = WebPage()

# === d 模式(浏览器控制)===
# 模拟登录,获取 cookie
page.get("https://example.com/login")
page.ele("#username").input("myuser")
page.ele("#password").input("mypass")
page.ele("button[type=submit]").click()
page.wait.url_change("https://example.com/dashboard")

# 此时浏览器已登录,cookies 自动同步到 s 模式

# === 切换到 s 模式(纯 requests,速度快 10 倍以上)===
page.change_mode("s")

# 用 requests 模式采集数据(自动携带浏览器的 cookies)
for i in range(1, 100):
    page.get(f"https://example.com/api/list?page={i}")
    data = page.json  # 直接解析 JSON
    print(data)

# 遇到需要 JS 渲染的页面再切回 d 模式
page.change_mode("d")

SessionPage — 纯请求模式

from DrissionPage import SessionPage

page = SessionPage()

# 类似 requests.Session,但 API 更统一
page.get("https://httpbin.org/get", params={"key": "value"})
print(page.json)

page.post("https://httpbin.org/post", json={"name": "Alice"})
print(page.response.status_code)

# 设置 headers
page.set.headers({"User-Agent": "Mozilla/5.0 ..."})
page.set.cookies({"session": "abc123"})

7. 监听网络请求(数据包监听)

from DrissionPage import ChromiumPage

page = ChromiumPage()

# 监听指定 URL 的请求(精确捕获 Ajax 数据)
page.listen.start("api/list")

page.get("https://example.com")
page.ele("button.load-more").click()

# 等待并获取数据包
packet = page.listen.wait()
print(packet.response.body)  # 响应体(JSON 数据)
print(packet.response.headers)
print(packet.url)

# 停止监听
page.listen.stop()

批量监听

page.listen.start("api/")

for _ in range(10):
    page.ele(".next-page").click()
    packet = page.listen.wait()
    data = packet.response.body
    process(data)

8. 多标签页 / 多实例

from DrissionPage import ChromiumPage, ChromiumOptions

# 多标签页
page = ChromiumPage()
tab1 = page  # 当前标签页
tab2 = page.new_tab("https://example.com/page2")

# 在不同标签页操作
tab1.ele(".item").click()
tab2.ele(".other").click()

# 多浏览器实例(爬虫并发)
from DrissionPage import ChromiumOptions

options = ChromiumOptions().auto_port()  # 自动分配端口

page1 = ChromiumPage(options)
page2 = ChromiumPage(options)

9. 反检测

from DrissionPage import ChromiumOptions

options = ChromiumOptions()

# 去除 webdriver 标记
options.set_argument("--disable-blink-features=AutomationControlled")

# 设置真实 User-Agent
options.set_user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...")

# 使用真实浏览器 Profile(含登录状态和历史记录)
options.set_user_data_path("/path/to/real/chrome/profile")

page = ChromiumPage(options)

# 注入 JS 隐藏 webdriver 特征
page.run_js("""
    Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
""")

10. 最佳实践

元素定位失败处理

# 默认等待 10 秒,可调整
ele = page.ele("#result", timeout=30)
if ele:
    print(ele.text)
else:
    print("元素未找到")

# 关闭等待(立即返回,找不到返回 None)
ele = page.ele("#result", timeout=0)

组合 ChromiumPage 与 requests(最高效模式)

import httpx
from DrissionPage import ChromiumPage

page = ChromiumPage()
# 浏览器登录...
cookies = {c["name"]: c["value"] for c in page.cookies()}

# 用 httpx 高并发采集(复用 cookies)
async with httpx.AsyncClient(cookies=cookies) as client:
    tasks = [client.get(f"/api/data?id={i}") for i in range(100)]
    results = await asyncio.gather(*tasks)

11. 踩坑与注意事项

元素定位优先用属性而非 XPath

DrissionPage 的属性定位比 XPath 更稳定,不受 DOM 层级变化影响:

# 不推荐(结构变化就失效)
page.ele("xpath://div[2]/form/input[1]")

# 推荐(语义稳定)
page.ele("@name=username")
page.ele("tag:input@placeholder=请输入用户名")

多实例必须用 auto_port

同时启动多个 ChromiumPage 时,不指定 auto_port() 会端口冲突:

options = ChromiumOptions().auto_port()
page = ChromiumPage(options)

最佳实践

优先用 ele() + eles() 取元素,而非 XPath 字符串拼接:DrissionPage 支持 @属性名=值text:文字 等简洁定位语法,比长串 XPath 更易维护。

# 推荐:简洁语法
btn = page.ele('@class=submit-btn')
inputs = page.eles('tag:input@type=text')
link = page.ele('text:立即登录')

# 低效:长 XPath
btn = page.ele('xpath://div[@class="form"]/button[@class="submit-btn"]')

接管已有浏览器调试现有页面:DrissionPage 可以接管用调试模式打开的浏览器,无需重新登录。

# 启动 Chrome 时加 --remote-debugging-port=9222
# chrome --remote-debugging-port=9222

options = ChromiumOptions().set_local_port(9222)
page = ChromiumPage(options)  # 接管已有浏览器

等待元素用 wait() 而非 time.sleep():固定 sleep 导致不必要的等待或在慢网络下不够等。wait.ele_loaded() 等待元素出现后立即继续。

# 正确:等待元素出现
page.wait.ele_loaded('#content', timeout=10)
ele = page.ele('#content')

# 低效:固定等待
import time
time.sleep(3)
ele = page.ele('#content')

SessionPage 处理纯 HTTP 请求以节省资源:不需要 JS 渲染的页面(静态 HTML、接口)用 SessionPageChromiumPage 快 10 倍以上,且不消耗浏览器资源。

Chromium 无头模式加 --no-sandbox:Docker 容器中运行必须加 --no-sandbox,否则启动失败。

options = ChromiumOptions().headless(True).set_argument('--no-sandbox')

常见陷阱

陷阱:ChromiumPage 多实例端口冲突

现象: 同时创建多个 ChromiumPage 时,第二个实例报端口被占用的错误。

原因: DrissionPage 默认使用固定端口(9222)连接 Chromium,多实例时端口冲突。

解决: 创建每个实例时调用 options.auto_port() 让系统自动分配空闲端口。

def make_page():
    opts = ChromiumOptions().auto_port()
    return ChromiumPage(opts)

page1, page2 = make_page(), make_page()

陷阱:ele() 找不到元素时返回 NoneType 而非抛异常

现象: 代码中 page.ele('#btn').click() 偶发 AttributeError: 'NoneType' object has no attribute 'click'

原因: 元素不存在时 ele() 默认返回 None,不抛异常,直接调用方法会 AttributeError。

解决: 先检查返回值是否为 None,或使用 wait.ele_loaded() 确保元素已加载。

btn = page.ele('#btn', timeout=5)
if btn:
    btn.click()
# 或
page.wait.ele_loaded('#btn', timeout=5).click()

陷阱:文件上传不能用 .click() + 文件对话框

现象: 点击上传按钮后弹出系统文件选择对话框,DrissionPage 无法操控该对话框。

原因: 系统原生文件对话框不是网页元素,无法通过 DOM 操控。

解决: 直接对 <input type="file"> 元素调用 .input() 传入文件路径,或使用 set_file_input() 方法。

# 正确:直接设置 input[type=file] 的值
file_input = page.ele('tag:input@type=file')
file_input.input('C:/path/to/file.jpg')

参见

阅读更多

Web 安全基础

1. HTML 转义(服务端渲染必须): 2. CSP(Content Security Policy): 3. HttpOnly Cookie:防止 JS 读取会话 Cookie: 4. 前端框架防护: 攻击者在第三方网站构造一个表单,诱导已登录用户提交,浏览器会自动携带目标站的 Cookie。 触发条件: 1. 用户已登录目标网站(Cookie 有效) 2. 目标 API 仅凭 Cookie 识别用户身份 3. 请求来源未验证 1. CSRF Token(推荐): 2. SameSite Cookie: 3. 验证 Origin/Referer 头:

By yellowdog

HTTP 协议深度指南

HTTP(HyperText Transfer Protocol)是 Web 的基础传输协议,基于 TCP/IP,采用请求/响应模型。 相关文档:Web安全基础(/web-an-quan-ji-chu/) FastAPI完全指南(/fastapi-wan-quan-zhi-nan/) Nginx完全指南(/nginx-wan-quan-zhi-nan/) 幂等性:多次执行相同请求,服务器状态结果相同。PUT /users/1 多次执行结果一致;POST /users 每次创建新资源,非幂等。 浏览器直接从本地缓存读取,不向服务器发送请求。 缓存命中时,状

By yellowdog

系统设计基础

SLA 对照表: 选择建议:无状态服务(Web 层、API 层)优先水平扩展;数据库初期垂直扩展,达到瓶颈后考虑分库分表或读写分离。 缓存穿透(查询不存在的 key,每次都打到 DB): 缓存击穿(热点 key 过期,瞬间大量请求打到 DB): 缓存雪崩(大量 key 同时过期,或缓存服务宕机): 令牌桶 Python 实现: Redis 实现分布式限流(滑动窗口): URL 命名规则: Cursor 分页响应格式: 雪花算法结构(64 bit): 定义:分布式系统不能同时满足以下三个特性: 在分布式环境中 P 是必须保证的,所以实际是 CP vs AP

By yellowdog

算法思路与模板

二分查找要求序列有序,每次将搜索范围缩减一半,时间复杂度 O(log n)。 两个指针从两端向中间收缩,常用于有序数组。 滑动窗口维护一个满足条件的区间 left, right,right 不断向右扩张,条件不满足时收缩 left。 滑动窗口通用框架: 1. 确定"子问题":原问题可以分解为哪些规模更小的同类问题 2. 定义 dpi 或 dpij 的含义,要足够清晰 3. 推导状态转移方程 4. 确定初始状态(边界条件) 5. 确定计算顺序(确保依赖的子问题先计算) 每件物品最多选一次。dpj = 容量为 j 时的最大价值,逆序遍历容量防止重复选取。 每

By yellowdog