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、接口)用 SessionPage 比 ChromiumPage 快 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')