法律与合规声明:本文仅讨论公开可访问数据的采集方法。在使用 DrissionPage 或任何爬虫工具前,请遵守目标网站的 robots.txt 和服务条款,并了解美国《计算机欺诈和滥用法》(CFAA)及欧盟《通用数据保护条例》(GDPR)的相关规定。如果目标平台提供官方 API,请优先使用。
如果你是一名 Python 爬虫开发者,大概率遇到过这样的困境:requests 速度快、内存占用低,但碰到 JS 渲染页面就无能为力;Selenium 或 Playwright 能渲染页面,但每个请求都启动浏览器实例,内存吃掉 2 GB,速度慢 10 倍。DrissionPage 正是为解决这个矛盾而生——它在一个框架内同时提供 HTTP 模式和浏览器模式,通过 WebPage 实现无缝切换并共享 cookies 和会话状态。本篇 DrissionPage 教程将深入讲解如何将住宅代理集成到这个双模式框架中,实现高效且不易被封的 DrissionPage web scraping 工作流。
DrissionPage 是什么:HTTP 与浏览器的统一框架
DrissionPage 是一个基于 Python 的自动化工具库,由国内开发者 g1879 创建并在 GitHub 开源。其核心设计理念是"一个工具,两种模式"。它通过 Chrome DevTools Protocol(CDP)直接与 Chromium 浏览器通信,无需 WebDriver 中间层,因此比 Selenium 更轻量、更快。
三种页面模型
DrissionPage 提供三种页面类型,各有适用场景:
- SessionPage:基于 Python
requests库的 HTTP 客户端模式。内存占用约 50 MB,适合采集静态页面或直接调用 JSON API。不支持 JavaScript 渲染。 - ChromiumPage:通过 CDP 协议控制 Chromium 浏览器。支持完整的 JS 渲染、截图、文件下载等操作。内存占用约 300-500 MB,适合处理动态渲染页面。DrissionPage Chromium 模式是处理现代 SPA 网站的关键能力。
- WebPage:DrissionPage 的核心创新。它同时持有 SessionPage 和 ChromiumPage 的能力,可以在运行时切换模式,且两种模式共享 cookies、headers 和会话状态。先用 HTTP 模式快速获取列表页,再切换到浏览器模式渲染详情页——无需重新登录或传递 cookies。
| 特性 | SessionPage | ChromiumPage | WebPage |
|---|---|---|---|
| 底层引擎 | requests (HTTP) | CDP → Chromium | 可切换 |
| JS 渲染 | 不支持 | 支持 | 按需启用 |
| 典型内存 | ~50 MB | ~300-500 MB | 取决于当前模式 |
| Cookie 共享 | 自身会话 | 浏览器上下文 | 跨模式共享 |
| 代理配置方式 | set_proxies() | ChromiumOptions.set_proxy() | 两种方式均支持 |
这种架构的经济学意义在于:大多数爬虫任务中,只有 10%-30% 的页面真正需要浏览器渲染。用 SessionPage 处理其余 70%-90% 的请求,可以将服务器成本降低 60% 以上。查看 ProxyHat 定价方案,结合你的并发需求估算代理成本。
DrissionPage 惯用 API:定位器、选项与监听器
ele() 与 eles() 元素定位
DrissionPage 提供了一套简洁的元素定位语法,比 Selenium 的 find_element 更直观:
# 按 ID
page.ele('#username')
# 按 class
page.ele('.search-box')
# 按属性
page.ele('@class=result-item')
page.ele('@type=submit')
# 按标签
page.ele('tag:input')
page.ele('tag:div@class=container')
# XPath
page.ele('xpath://div[@class="content"]/p[1]')
# 获取多个元素
items = page.eles('tag:article')
for item in items:
print(item.text)
ChromiumOptions 配置
ChromiumOptions 是 ChromiumPage 的配置中心,用于设置浏览器路径、启动参数、代理等:
from DrissionPage import ChromiumOptions
co = ChromiumOptions()
co.set_argument('--no-sandbox')
co.set_argument('--disable-gpu')
co.set_argument('--window-size', '1920,1080')
co.headless() # 无头模式
listen.start() 监听网络请求
这是 DrissionPage 最强大的功能之一——监听浏览器发出的后台 XHR/fetch 请求,直接捕获 JSON 数据,无需解析渲染后的 HTML:
from DrissionPage import WebPage
page = WebPage()
page.listen.start('api/products') # 监听匹配 URL 的请求
page.get('https://example.com/shop')
# 等待并获取数据包
packet = page.listen.wait(timeout=10)
if packet:
print(packet.url) # 请求 URL
print(packet.method) # GET / POST
print(packet.response.body) # 响应体(通常为 JSON)
page.listen.stop()
通过 listen 捕获后台 API 响应,你可以跳过 HTML 解析,直接获取结构化 JSON 数据。这在 DrissionPage web scraping 中是一个关键技巧——许多现代网站通过 XHR 加载数据,监听网络流量比解析 DOM 更可靠。
配置 DrissionPage 代理:SessionPage 与 ChromiumPage
在 DrissionPage 中配置代理需要区分两种模式。SessionPage 使用 set.proxies() 方法,而 ChromiumPage 通过 ChromiumOptions.set_proxy() 设置。DrissionPage proxy 配置的核心区别在于:HTTP 模式走 requests 的代理机制,浏览器模式走 Chromium 的代理机制。
SessionPage 代理配置
from DrissionPage import SessionPage
page = SessionPage()
page.set.proxies({
'http': 'http://user-country-US:pass@gate.proxyhat.com:8080',
'https': 'http://user-country-US:pass@gate.proxyhat.com:8080'
})
page.get('https://httpbin.org/ip')
print(page.json)
ChromiumPage 代理配置
from DrissionPage import ChromiumPage, ChromiumOptions
co = ChromiumOptions()
co.set_proxy('http://user-country-US:pass@gate.proxyhat.com:8080')
co.headless()
page = ChromiumPage(co)
page.get('https://httpbin.org/ip')
print(page.ele('tag:pre').text)
对于硬目标网站(如电商、社交媒体、票务平台),数据中心 IP 往往被直接封禁。住宅代理使用真实 ISP 分配的 IP 地址,请求看起来来自普通家庭用户,因此成功率显著更高。ProxyHat 提供覆盖全球的住宅代理网络,支持国家/城市级地理定位,查看 可用代理位置 了解覆盖范围。
完整示例:WebPage 双模式 + ProxyHat 住宅代理
下面是一个完整的可运行示例,展示 WebPage 如何在 HTTP 模式和浏览器模式之间切换,同时保持代理和会话状态。我们使用 ProxyHat 的住宅代理端点,通过用户名参数实现地理定位和会话固定。
首先,创建一个 ProxyHat 用户名构建器:
class ProxyHatConfig:
GATEWAY = 'gate.proxyhat.com'
HTTP_PORT = 8080
SOCKS5_PORT = 1080
@staticmethod
def build_username(country=None, city=None, session=None):
parts = ['user']
if country:
parts.append(f'country-{country}')
if city:
parts.append(f'city-{city}')
if session:
parts.append(f'session-{session}')
return '-'.join(parts)
@classmethod
def http_url(cls, country=None, city=None, session=None):
username = cls.build_username(country, city, session)
return f'http://{username}:pass@{cls.GATEWAY}:{cls.HTTP_PORT}'
@classmethod
def socks5_url(cls, country=None, city=None, session=None):
username = cls.build_username(country, city, session)
return f'socks5://{username}:pass@{cls.GATEWAY}:{cls.SOCKS5_PORT}'
然后,使用 WebPage 实现双模式采集:
from DrissionPage import WebPage, ChromiumOptions
import uuid
# 构建 ProxyHat 住宅代理 URL
session_id = str(uuid.uuid4())[:8]
proxy_url = ProxyHatConfig.http_url(
country='US',
session=session_id
)
# 创建 WebPage,初始使用 HTTP 模式
page = WebPage()
page.set.proxies({
'http': proxy_url,
'https': proxy_url
})
# 第一步:HTTP 模式获取列表页(快速、低开销)
page.get('https://example.com/products?page=1')
links = page.eles('tag:a@class=product-link')
product_urls = [link.attr('href') for link in links[:5]]
print(f'HTTP 模式获取到 {len(product_urls)} 个产品链接')
# 第二步:切换到 Chromium 模式渲染 JS 详情页
co = ChromiumOptions()
co.set_proxy(proxy_url)
co.headless()
page.change_mode(co) # 切换到浏览器模式,cookies 自动传递
# 监听后台 API
page.listen.start('api/product-detail')
for url in product_urls[:3]:
page.get(url)
packet = page.listen.wait(timeout=15)
if packet:
data = packet.response.body
print(f'产品数据: {data[:200]}')
else:
# 降级为 DOM 解析
title = page.ele('h1').text
print(f'产品名称: {title}')
page.listen.stop()
page.quit()
这个示例展示了 DrissionPage 的核心优势:先用 SessionPage 快速抓取列表页(每个请求约 200ms),再切换到 ChromiumPage 渲染需要 JS 执行的详情页。代理在两种模式下保持一致,cookies 自动传递,无需手动管理会话。
生产级模式:会话固定、重试与并发
会话固定(Sticky Session)
许多网站要求同一会话使用同一 IP(登录后操作、多步表单等)。ProxyHat 支持通过用户名中的 session-xxx 参数固定 IP。在 DrissionPage 中,为每个 WebPage 实例分配独立的 session ID:
import uuid
from DrissionPage import WebPage
def create_pinned_session(country='US'):
session_id = str(uuid.uuid4())[:8]
proxy = ProxyHatConfig.http_url(
country=country,
session=session_id
)
page = WebPage()
page.set.proxies({'http': proxy, 'https': proxy})
return page, session_id
# 每个任务使用独立的 sticky session
page, sid = create_pinned_session('US')
# 同一 session_id 在有效期内保持同一出口 IP
重试与错误处理
import time
from DrissionPage.errors import ElementNotFoundError
def safe_get(page, url, max_retries=3):
for attempt in range(max_retries):
try:
resp = page.get(url, timeout=15)
if resp and page.status_code == 200:
return page
print(f'尝试 {attempt+1} 失败: HTTP {page.status_code}')
except ElementNotFoundError:
print(f'尝试 {attempt+1} 失败: 元素未找到')
except Exception as e:
print(f'尝试 {attempt+1} 失败: {e}')
time.sleep(2 ** attempt) # 指数退避
return None
并发控制
DrissionPage 本身不提供并发管理,但可以与 concurrent.futures 或 asyncio 配合使用。关键原则:
- SessionPage 模式下,每个线程使用独立的 WebPage 实例和独立代理 session ID。
- ChromiumPage 模式下,每个浏览器实例约占用 300-500 MB 内存。一台 16 GB 内存的服务器建议最多运行 20-30 个并发浏览器实例。
- 设置合理的请求间隔(1-3 秒),避免触发目标网站的频率限制。
- 使用 ProxyHat 的旋转代理模式(不指定 session)实现每请求换 IP,适合大规模 SERP 采集。参考 SERP 跟踪用例 了解更多。
from concurrent.futures import ThreadPoolExecutor, as_completed
import uuid
def scrape_task(url):
session_id = str(uuid.uuid4())[:8]
proxy = ProxyHatConfig.http_url(country='US', session=session_id)
page = WebPage()
page.set.proxies({'http': proxy, 'https': proxy})
try:
page.get(url, timeout=15)
result = page.ele('h1').text if page.ele('h1') else 'N/A'
return url, result
finally:
page.close()
urls = ['https://example.com/page/1', 'https://example.com/page/2']
with ThreadPoolExecutor(max_workers=10) as executor:
futures = [executor.submit(scrape_task, url) for url in urls]
for f in as_completed(futures):
url, result = f.result()
print(f'{url}: {result}')
通过 listen 发现隐藏 API
许多现代网站将数据放在后台 XHR 请求中,前端只负责渲染。通过 listen.start() 监听网络流量,你可以发现这些隐藏的 API 端点,然后直接用 SessionPage 调用它们——无需浏览器渲染。这是从 ChromiumPage 降级到 SessionPage 的关键发现手段。
# 第一步:用浏览器模式监听,发现 API
page = ChromiumPage(co)
page.listen.start('api/') # 监听所有 API 请求
page.get('https://target-site.com')
# 收集所有 API 端点
for _ in range(10):
pkt = page.listen.wait(timeout=5)
if pkt:
print(f'发现 API: {pkt.url} -> {pkt.response.status}')
page.listen.stop()
page.quit()
# 第二步:切换到 SessionPage 直接调用 API
sp = SessionPage()
sp.set.proxies({'http': proxy_url, 'https': proxy_url})
sp.get('https://target-site.com/api/data?page=1')
data = sp.json # 直接获取 JSON
什么时候不要升级到浏览器
DrissionPage 的双模式设计很强大,但并非所有场景都需要浏览器。以下情况应保持 SessionPage 模式:
- 目标页面是静态 HTML:如果页面源码中已包含所需数据,用 SessionPage 即可,延迟约 200ms,而浏览器渲染需要 2-5 秒。
- 已发现后台 API:通过
listen发现 JSON API 后,直接用 SessionPage 调用,效率高 10 倍以上。 - 大规模列表页采集:数千个列表页用 HTTP 模式可以在几分钟内完成,浏览器模式则需数小时。
- 仅需 HTTP header 交互:设置 cookies、发送 POST 请求等,SessionPage 完全够用。
反之,以下情况需要升级到 ChromiumPage:
- 页面数据由 JavaScript 动态生成,源码中不存在。
- 需要与页面交互(点击、滚动、填表)。
- 目标网站使用 Cloudflare Turnstile 或 reCAPTCHA 等 JS 验证。
- 需要截图或下载文件。
更多爬虫场景的最佳实践,参考 Web Scraping 用例页面。ProxyHat 的 官方文档 提供了完整的代理配置参数说明。
关键要点
- 双模式是核心价值:DrissionPage 的 WebPage 让你在 SessionPage(HTTP)和 ChromiumPage(浏览器)之间无缝切换,共享 cookies 和代理,按需付费——只在必要时才启动浏览器。
- 代理配置分两路:SessionPage 用
set.proxies(),ChromiumPage 用ChromiumOptions.set_proxy()。ProxyHat 住宅代理通过gate.proxyhat.com:8080接入。- listen 是杀手锏:用
listen.start()监听浏览器网络流量,发现隐藏 API 后降级到 HTTP 模式,效率提升 10 倍以上。- 会话固定很重要:通过用户名中的
session-xxx参数为每个任务分配独立 sticky session,避免 IP 频繁切换导致封号。- 合规先行:只采集公开数据,遵守 robots.txt 和服务条款,了解 CFAA 和 GDPR 的法律边界。
DrissionPage + ProxyHat 住宅代理的组合,为 Python 爬虫开发者提供了一个兼具效率和隐蔽性的解决方案。从 ProxyHat 定价方案 开始,选择适合你业务规模的代理套餐,然后按照本教程的示例快速集成到你的 DrissionPage 项目中。






