
简介DrissionPage 是一款面向 Python 开发者、RPA 工程师及网络爬虫实践者的网页自动化工具融合浏览器控制与 HTTP 请求能力解决动态页面交互、JS 渲染内容抓取、表单自动提交等典型自动化难题兼顾易用性与扩展性适合中初级开发者快速上手也支持高级用户二次开发。资源为源码级压缩包zip共 70 个文件含 36 个核心 Python 模块如 chromium_page.py、session_page.py、action_chains.py 等、28 个类型提示文件.pyi以及文档index.html、配置.ini、许可证LICENSE和构建文件setup.py、MANIFEST.in整体仅 180KB轻量紧凑结构清晰便于阅读源码逻辑与理解混合页面操作机制。已有 691 人学习下载。读者可直接运行调试完整框架掌握 Chromium 驱动封装、会话页与混合页双模式设计、元素定位与链式操作等关键实现并借助内置 docs 和示例结构快速构建 RPA 流程或高稳定性爬虫脚本。1. DrissionPage 不是另一个 Selenium 封装它是把浏览器控制权真正交还给 Python 的网页自动化新路径你写过 Selenium也试过 Playwright但每次遇到 iframe 嵌套跳转、页面资源加载阻塞、或需要在 JS 执行中途插入 Python 逻辑时总得绕一圈切 context、等 readyState、手动注入 script、再 parse 返回值——像隔着一层毛玻璃操作浏览器。DrissionPage 的出现不是为替代谁而是把「Python 进程直接驱动 Chromium」这件事做透它用requests级的 HTTP 控制能力 DevTools Protocol级的浏览器底层指令让page.ele(xpath//button).click()背后没有 WebDriver 协议转换没有 JSON Wire 协议封装也没有隐式等待的黑盒调度。它适合两类人一类是爬虫工程师需要稳定抓取含反爬 JS 渲染页如电商商品详情、金融行情页另一类是 RPA 开发者要批量处理内部 Web 系统表单提交、审批流点击、PDF 导出触发等强交互场景。它不追求“跨浏览器兼容”只专注把 Chromium 的能力用 Python 原生语法调到最稳——这正是当前 rpa 网页自动化 和 python爬虫 领域里越来越多人放弃“通用抽象层”、转向“精准控制”的真实需求。2. 为什么选 DrissionPage 而非 Selenium/Playwright从协议栈到底层控制力的三重差异2.1 协议层级决定响应粒度HTTP 请求直连 vs 中间协议代理Selenium 依赖 WebDriver 协议所有操作点击、输入、截图都需经由浏览器驱动chromedriver/geckodriver转发每一步都经历“Python → WebDriver API → JSON Wire 协议序列化 → 驱动进程解析 → DevTools Protocol 调用 → 浏览器执行”五层链路。而 DrissionPage 绕过 WebDriver直接通过httpx或requests向 Chromium 的 DevTools ProtocolCDP端口默认localhost:9222/json发送原始 JSON-RPC 请求。例如获取页面标题# Selenium 写法经 WebDriver 协议 driver.title # 隐式触发 GET /session/{id}/title # DrissionPage 写法直连 CDP from DrissionPage import ChromiumPage page ChromiumPage() page.title # 底层调用: POST http://localhost:9222/json → 解析 tabs 列表 → 提取 title 字段提示DrissionPage 的page.title不是读取 DOM而是直接从 CDP 的Target.getTargets响应中提取毫秒级返回且不受页面 JS 是否执行完毕影响。这对监控型任务如实时检测页面 title 变更至关重要。2.2 页面加载控制权回归 Pythonwait不是等待而是主动决策Selenium 的WebDriverWait(driver, 10).until(EC.presence_of_element_located(...))本质是轮询 DOM每 500ms 发一次document.querySelector(...)。DrissionPage 提供wait.ele_exists()但底层逻辑完全不同# DrissionPage 的等待是 CDP 事件监听 Python 条件判断 page.wait.ele_exists(css.product-price, timeout8) # 实际执行 # 1. 向 CDP 发送 Page.enable → 监听 DOMContentLoaded、Load、FrameNavigated 事件 # 2. 同时启动 Python 线程每 200ms 执行一次 page._run_js(return !!document.querySelector(.product-price);) # 3. 一旦返回 True立即终止监听并返回元素对象这种混合模式让等待行为可中断、可嵌套、可与协程共存。例如在异步任务中import asyncio from DrissionPage import ChromiumPage async def check_price_update(page): while True: # 主动检查价格是否变化而非被动等待 current await page.run_async(return document.querySelector(.price).innerText) if current ! last_price: print(fPrice updated to {current}) last_price current await asyncio.sleep(1) # 非阻塞休眠2.3 元素定位不再依赖“查找-返回-包装”ele对象即上下文容器Selenium 的WebElement是远程引用所有.click().send_keys()都需序列化后发往驱动。DrissionPage 的ChromiumElement是本地 Python 对象封装了该元素在 CDP 中的backendNodeId和frameId所有操作直接构造 CDP 指令操作Selenium 底层调用DrissionPage 底层调用ele.click()Input.dispatchMouseEvent需计算坐标DOM.resolveNodeInput.dispatchMouseEvent坐标由 CDP 自动计算ele.textelement.getText()WebDriver 协议Runtime.evaluate(return arguments[0].textContent)直接 JS 执行ele.screenshot()GET /session/{id}/screenshot/{id}Page.captureScreenshotCDP 原生命令支持 fullPagetrue这意味着ele不是“找到的节点”而是“已绑定上下文的控制句柄”。你可以安全地在 iframe 切换后继续使用原ele对象因为它的frameId会随页面导航自动更新。3. 用 DrissionPage 在本地跑通电商详情页抓取的最小命令从安装到结构化解析3.1 安装与环境校验避开 Windows 下常见的 Chromium 版本错配坑DrissionPage 默认使用系统已安装的 Chrome/Chromium但多数用户直接pip install DrissionPage后运行报错chrome not found。正确做法分三步# 步骤1确认系统 Chrome 路径Windows 常见路径 where chrome # 输出示例C:\Program Files\Google\Chrome\Application\chrome.exe # 步骤2设置环境变量永久生效 # Windows PowerShell管理员运行 [Environment]::SetEnvironmentVariable(DRIVER_PATH, C:\Program Files\Google\Chrome\Application\chrome.exe, Machine) # Linux/macOS echo export DRIVER_PATH/usr/bin/chromium-browser ~/.bashrc source ~/.bashrc # 步骤3验证安装关键 python -c from DrissionPage import ChromiumOptions; opt ChromiumOptions(); print(opt.browser_path) # 必须输出非空路径否则后续所有操作失败注意不要用--headlessnew参数启动无头模式来测试基础功能。DrissionPage 的ChromiumPage默认启用 GUI因为部分反爬检测如navigator.webdriver在无头模式下极易触发。先确保有界面能打开再优化为无头。3.2 抓取京东商品详情页处理动态加载、iframe 嵌套与懒加载图片以京东 URLhttps://item.jd.com/100012043978.html为例其价格、库存、评论数均通过 AJAX 加载商品图位于 iframe 内主图区域有滚动懒加载from DrissionPage import ChromiumPage import time page ChromiumPage() # 1. 访问页面并等待骨架屏消失非等待 DOM而是监听 network 请求完成 page.get(https://item.jd.com/100012043978.html) page.wait.network_idle() # 等待所有 pending 请求完成比 wait.doc_loaded 更准 # 2. 处理主商品图 iframe京东将大图放在 iframe 中防爬 iframe page(xpath://iframe[contains(src,cdn)]) page.switch_to.frame(iframe) # 3. 获取高清主图懒加载需滚动触发 main_img page.ele(css.spec-items img) page.scroll.to_see(main_img) # 滚动到元素可见区域 time.sleep(0.5) # 等待 lazyload 触发 img_url main_img.attr(src) or main_img.attr(data-src) # 4. 切回主文档抓取价格AJAX 加载需等待元素出现 page.switch_to.parent_frame() price_ele page.wait.ele_exists(cssspan.p-price, timeout10) price price_ele.ele(cssspan.price).text.strip() print(f商品价格{price}主图地址{img_url})关键参数说明表参数/方法作用推荐值错误用法示例page.wait.network_idle()等待所有网络请求完成含 XHR、fetchtimeout15page.wait.doc_loaded()仅等 HTML 解析不等 JS 加载数据page.scroll.to_see(ele)滚动使元素进入视口触发懒加载无参数page.scroll.to_bottom()粗暴滚动可能错过目标元素page.switch_to.frame(iframe)切入 iframe 上下文必须传入 iframe 元素对象page.switch_to.frame(iframe_id)DrissionPage 不支持字符串 ID 切换ele.attr(data-src)获取属性值优先># 开新 tab 抓取评论避免主页 DOM 干扰 comment_tab page.new_tab() comment_tab.get(https://club.jd.com/comment/productPageComments.action?callbackfetchJSON_comment98vv123productId100012043978score0sortType5page0pageSize10) # 解析 JSONP 响应 raw_json comment_tab._run_js(return fetchJSON_comment98vv123) comments raw_json[comments] # 关闭 tab 释放内存 comment_tab.close() # 主页 tab 仅处理商品基础信息 basic_info { title: page.ele(cssh1.item-name).text, price: page.ele(cssspan.p-price).ele(cssspan.price).text, sku: page(cssdiv#parameter-brand).text } import json with open(jd_product.json, w, encodingutf-8) as f: json.dump({basic: basic_info, comments: comments[:5]}, f, ensure_asciiFalse, indent2)4. DrissionPage 的 3 个必调参数解决滑动滚轮失效、iframe 切换丢失、JS 执行超时4.1scroll.to_see()失效用offset和smooth精确控制滚动行为page.scroll.to_see(ele)在某些页面如 Vue 渲染的瀑布流会因元素位置计算偏差而失效。根本原因是 CDP 的DOM.getBoxModel返回坐标基于渲染树而 JSgetBoundingClientRect()基于布局树二者在复杂 CSS 下存在像素级偏移。解决方案是手动指定偏移量# 原始失效写法 page.scroll.to_see(page.ele(css.review-item)) # 修正后向下多滚 200px确保进入视口 ele page.ele(css.review-item) page.scroll.to_see(ele, offset(0, 200)) # 或启用平滑滚动避免触发反爬滚动检测 page.scroll.to_see(ele, smoothTrue)smoothTrue会调用window.scrollTo({top: y, behavior: smooth})比原生scrollIntoView更接近人工操作且可被网站scroll事件监听器捕获——这对需要模拟真实用户行为的 RPA 场景很关键。4.2 iframe 切换后ele对象失效用ChromiumFrame显式管理上下文当页面动态插入 iframe如点击“查看全部评论”后加载新 iframepage.switch_to.frame()切换后之前获取的ChromiumElement对象仍绑定旧 frameId调用.click()会报No node with given id found。正确做法是获取ChromiumFrame对象并复用# 错误切换后直接用旧 ele iframe page.ele(css#review-iframe) page.switch_to.frame(iframe) old_ele page.ele(css.btn-submit) # 此时 old_ele.frame_id 已过期 old_ele.click() # 报错 # 正确用 ChromiumFrame 管理 frame page.get_frame(css#review-iframe) # 返回 ChromiumFrame 对象 submit_btn frame.ele(css.btn-submit) # 元素绑定 frame.frame_id submit_btn.click() # 安全执行ChromiumFrame是 DrissionPage 专为 iframe 设计的上下文容器其.ele()方法生成的元素自动继承 frame 的frameId无需手动切换。4.3run_js()执行超时用timeout和as_expr区分脚本类型page.run_js(return document.title)默认超时 10 秒但某些页面 JS 执行慢如含大量计算的加密函数需显式设参# 执行简单表达式推荐用 as_exprTrue性能提升 3 倍 title page.run_js(document.title, as_exprTrue) # 直接返回值不包装成 Promise # 执行复杂脚本需设 timeout避免卡死 encrypted page.run_js( function encrypt(str) { /* 大量计算 */ } return encrypt(data); , timeout30) # 设为 30 秒防止阻塞 # 执行异步脚本必须用 await as_exprFalse result page.run_js( return new Promise(resolve { setTimeout(() resolve(done), 2000); }); , as_exprFalse) # as_exprFalse 才支持 Promiseas_exprTrue告诉 DrissionPage 这是一条表达式非函数CDP 直接调用Runtime.evaluate的expression字段省去function(){...}()包裹as_exprFalse则走完整Runtime.callFunctionOn流程支持await和Promise。5. drissionpage 滑动滚轮进阶技巧模拟真实用户轨迹与反检测绕过5.1 用scroll.smooth_move()实现贝塞尔曲线滚动绕过滚动行为检测部分网站如知乎、小红书通过监听wheel事件的deltaY和timeStamp间隔识别程序化滚动。DrissionPage 的scroll.smooth_move()支持自定义缓动函数# 模拟人类滚动先快后慢带随机停顿 page.scroll.smooth_move( to_y5000, duration3000, # 总时长 3 秒 easecubic-bezier(0.34, 1.15, 0.56, 1), # 加速-减速曲线 pause_at[1000, 2000] # 在 1s、2s 处各停顿 200ms ) # 验证是否触发了页面懒加载 loaded_imgs page.eles(cssimg[data-src][src]:not([src])) print(f滚动后加载图片数{len(loaded_imgs)})ease参数接受 CSS easing 函数字符串pause_at是毫秒级时间点列表。该方法底层调用window.scrollTo()配合requestAnimationFrame比scroll.to_see()更贴近人工操作。5.2 结合ChromiumPage的set.cookies()与滚动维持登录态下的权限访问电商后台、企业 OA 系统常要求登录后才能滚动查看更多数据。DrissionPage 可在滚动前注入 cookies# 从 requests.Session 同步 cookies适用于已登录的 requests 会话 import requests sess requests.Session() sess.post(https://oa.example.com/login, data{user: admin, pwd: 123}) # 将 sess cookies 注入 DrissionPage page.set.cookies(sess.cookies.get_dict(), domainoa.example.com) # 滚动时保持登录态 page.get(https://oa.example.com/approval/list) page.scroll.smooth_move(to_y10000, duration5000) # 所有后续请求包括滚动触发的 AJAX自动携带 cookiespage.set.cookies()直接调用 CDP 的Network.setCookies比 Selenium 的add_cookie()更底层且支持sameSite、secure等高级属性。5.3 滚动过程中的元素状态监控用ChromiumPage.listen.start()捕获动态加载事件当滚动触发无限加载infinite scroll时需监听新元素插入。DrissionPage 的listen模块可捕获 DOM 变更# 启动监听捕获 .item-card 类元素新增 page.listen.start(div.item-card) page.scroll.smooth_move(to_y8000, duration4000) # 获取所有捕获到的新元素 new_cards page.listen.steps(timeout5) for card in new_cards: title card.ele(cssh3).text price card.ele(cssspan.price).text print(f新加载商品{title} ¥{price}) # 停止监听释放资源 page.listen.stop()listen.start()底层使用 CDP 的DOM.performSearchDOM.getSearchResults比轮询page.eles()效率高 10 倍以上且能精确捕获插入时机避免漏抓。本文还有配套的精品资源点击获取