专用于 AI 分析和数据采集场景,可拦截任意请求响应包的 Firefox 自动化库。
Project description
ruyiPage
专用于 AI 分析 和 数据采集 场景,可拦截任意请求响应包。
Built for AI analysis and data capture workflows, with the ability to intercept arbitrary request and response packets.
下一代自动化框架
- 自带过检测火狐内核
- 大量
isTrusted原生动作,无自动化检测点- 支持多种 JS 事件构造附加
ruyi: true,让Event/InputEvent/MouseEvent/KeyboardEvent等事件的isTrusted更贴近真实交互- 支持 ADS 等指纹浏览器直接自动化接管
- 基于 Firefox + WebDriver BiDi
- 更适合高风控场景
请我喝咖啡
如果这个项目对你有帮助,欢迎请我喝杯咖啡,支持我继续完善 ruyiPage。
|
公众号 |
QQ 社群 |
联系我 / 个人微信 |
请我喝咖啡 |
配套项目
如果你准备把 ruyiPage 用在 AI 自动化分析、复杂网页采集或高风控页面场景,建议先看这两个配套项目:
- AI 自动化分析运行 Skill
面向 AI 协作和自动化分析场景的运行说明与实践入口,适合先了解如何把
ruyiPage接进你的工作流:https://github.com/LoseNine/ruyipage-skill?tab=readme-ov-file - Firefox 指纹浏览器项目
用于需要 Firefox 指纹环境、浏览器接管或更高真实度自动化场景,适合和
ruyiPage搭配使用:https://github.com/LoseNine/firefox-fingerprintBrowser
实战展示
下面这些图放的是实际场景展示。为了在 GitHub 首页里更紧凑,我这里用两列表格展示。
| 可直接通过 Cloudflare 5s 盾 |
可直接通过 hCaptcha |
| 可直接通过 DataDome |
可直接进入 Outlook Mail |
| 可直接进入 Google Mail |
bet365 实战展示 |
| 指纹浏览器指纹页展示 |
Firefox 路线真实场景能力 更适合高风控页面、登录流、验证码与真实交互场景 |
这些展示图用于说明
ruyiPage在 Firefox 路线下的真实场景能力。 如果目标站点风控更强,仍建议优先配合本项目推荐的 Firefox 内核方案,或任意可用的火狐指纹浏览器使用。
安装与使用
安装
pip install ruyiPage --upgrade
如果你是首次安装,也可以直接用上面的命令获取最新版。
如果你是从源码运行,或给学员分发项目源码,建议同时安装项目依赖:
pip install -r requirements.txt
安装后建议先确认:
python -c "import ruyipage; print(ruyipage.__version__)"
最简单启动
from ruyipage import FirefoxPage
page = FirefoxPage()
page.get("https://www.example.com")
print(page.title)
page.quit()
JS 事件 isTrusted 对比能力
ruyiPage 不只是支持原生点击、输入、悬停这类高 isTrusted 动作,也支持在多种 JS 事件构造里附加 ruyi: true,用于让事件的 isTrusted 表现与真实交互更一致。
例如:
new Event('change', { bubbles: true, ruyi: true })
new InputEvent('input', { bubbles: true, data: 'A', inputType: 'insertText', ruyi: true })
new MouseEvent('click', { bubbles: true, clientX: 12, clientY: 24, ruyi: true })
new KeyboardEvent('keydown', { bubbles: true, key: 'Enter', code: 'Enter', ruyi: true })
可直接运行综合示例:
python examples/45_js_setter_untrusted_input.py
这个示例会对比普通 JS 事件与 ruyi: true 事件的 isTrusted,覆盖:
EventInputEventKeyboardEventMouseEventFocusEventCustomEventPointerEventWheelEvent
指定 Firefox 路径和 userdir
from ruyipage import FirefoxOptions, FirefoxPage
opts = FirefoxOptions()
opts.set_browser_path(r"D:\Firefox\firefox.exe")
opts.set_user_dir(r"D:\ruyipage_userdir")
page = FirefoxPage(opts)
page.get("https://www.example.com")
print(page.title)
page.quit()
更适合新手的 launch
from ruyipage import launch
page = launch(
browser_path=r"D:\Firefox\firefox.exe",
user_dir=r"D:\ruyipage_userdir",
headless=False,
port=9222,
)
page.get("https://www.example.com")
print(page.title)
page.quit()
开启隐私模式
from ruyipage import FirefoxOptions, FirefoxPage, launch
# 方式一:在配置对象上开启 Firefox 私密浏览模式
opts = FirefoxOptions()
opts.private_mode(True)
page = FirefoxPage(opts)
page.get("https://www.example.com")
page.quit()
# 方式二:直接用 launch()
page = launch(private=True)
page.get("https://www.example.com")
page.quit()
说明:
private=True/opts.private_mode(True)会为 Firefox 增加-private启动参数- 这和默认的临时
profile不是一回事 - 如果你只是想要一次性会话,不复用历史数据,不传
user_dir也可以 - 完整示例可参考根目录:
quickstart_private_mode.py
启用 XPath Picker
from ruyipage import FirefoxOptions, FirefoxPage, launch
# 方式一:在 FirefoxOptions 上开启
opts = FirefoxOptions()
opts.enable_xpath_picker(True)
page = FirefoxPage(opts)
page.get("https://www.example.com")
# 方式二:直接用 launch()
page = launch(xpath_picker=True)
page.get("https://www.example.com")
启用后,页面右下角会出现一个半透明磨砂玻璃浮窗:
- 点击页面元素时,会锁定当前结果
- 浮窗会显示元素名字、文本、XPath 绝对路径、XPath 相对路径、元素中心
(x, y) - 内置
ruyiPage代码生成选项卡,会自动生成对应元素获取代码 - iframe、嵌套 iframe、open shadow root 场景会自动拼好访问链
XPath (absolute)、XPath (relative)、ruyiPage代码生成都支持一键复制- 锁定后不会继续切换到其他元素
- 点击浮窗里的
继续选择后,才会重新允许选择其他元素 - 点击浮窗里的
暂停选择可暂时停止拦截页面点击 - 点击浮窗里的
收起可折叠为右下角小胶囊,再点击即可展开
推荐直接运行用户示例:
python examples/42_xpath_picker_complex_showcase.py
这个示例会打开一套专门的复杂测试页面,覆盖:
- 普通页面元素
- 同源 iframe / 嵌套 iframe
- open shadow root
- 复杂文本节点与 SVG 节点
接管已打开的浏览器
如果 Firefox 已经是你手动打开的,或者是指纹浏览器先打开的,也可以直接接管现有实例。
这套方式适用于任意 Firefox 内核指纹浏览器,包括 ADS / FlowerBrowser 这类产品。 如果浏览器允许固定启动参数,建议加入:
--remote-debugging-port=9222
如果后台会把它改写成随机端口,也可以直接使用按进程特征的自动探测接管。
from ruyipage import auto_attach_exist_browser_by_process
page = auto_attach_exist_browser_by_process(
latest_tab=True,
)
print(page.browser.address)
print(page.title)
print(page.url)
适合这些情况:
- 你想先手动打开 Firefox,再让
ruyiPage接管 - 你想先启动指纹浏览器,再从业务脚本里连进去
- 你使用的是 ADS / FlowerBrowser,真实调试端口会随机变化
- 你不想手动维护端口范围,希望直接按 Firefox 进程特征自动探测
浏览器路径和 userdir 是什么
browser_path
Firefox 可执行文件路径。
适合这些情况:
- Firefox 不在默认安装目录
- 你有多个 Firefox 版本
- 你用便携版 Firefox
user_dir
也就是 Firefox 的 profile / 用户目录。
适合这些情况:
- 想复用登录状态
- 想保留 Cookie / 本地存储
- 想复用扩展、证书、首选项
如果不设置:
ruyiPage会自动创建临时 profile- 适合一次性测试
- 关闭后通常会被清理
项目定位
ruyiPage 是一个面向 Firefox 浏览器自动化 的 Python 库,底层协议来自:
- WebDriver BiDi: https://w3c.github.io/webdriver-bidi/
如果你想找的不是“又一个 CDP 自动化库”,而是:
- 新一代 Firefox 自动化框架
- 基于 BiDi 协议,而不是 CDP 协议
- 原生动作能实现大量
isTrusted行为 - 内置拟人化行为能力,适合高风控页面
- 支持网络劫持、拦截、mock、collector 等能力
- 支持单浏览器多 tab 使用不同 user context,隔离 Cookie / 存储上下文
- 更适合做长期维护的 Firefox 自动化体系
那么 ruyiPage 就是为这个方向准备的。
你可以把它理解成:
面向 Firefox 的高层自动化框架,核心思想是 用 WebDriver BiDi 做底层、用新手易用 API 做上层。
它尤其强调四件事:
- 不依赖 CDP,天然没有 CDP 路线的那层暴露面
- 原生动作链优先,尽量让输入、拖拽、点击等行为保持
isTrusted - 内置拟人行为能力,更适合高风控页面的真实交互场景
- 支持 user context 隔离,适合同浏览器多账号、多会话并行
- 高层 API 可直接上手,更适合新手和团队统一维护
能力总览
在看详细文档前,你可以先看这张总表,快速了解 ruyiPage 现在已经能做什么。
| 能力大类 | 高层入口 | 典型能力 |
|---|---|---|
| 页面导航 | page.get() / page.back() / page.forward() |
打开页面、刷新、前进后退 |
| 元素查找 | page.ele() / page.eles() / ele.ele() |
CSS/XPath/Text 定位、容器内继续查找 |
| 元素交互 | ele.click_self() / ele.input() / ele.attr() / ele.text |
点击、输入、取属性、读文本 |
| 动作链 | page.actions |
键盘、鼠标、拖拽、滚轮、拟人动作 |
| 触摸输入 | page.touch |
tap、long press 等触摸操作 |
| Cookies | page.get_cookies() / page.set_cookies() / page.delete_cookies() |
读取、设置、删除 Cookie |
| 下载 | page.downloads |
设置下载目录、等待下载事件、验证落盘 |
| PDF / 截图 | page.save_pdf() / page.screenshot() |
页面打印 PDF、保存截图 |
| 弹窗处理 | page.wait_prompt() / page.accept_prompt() / page.set_prompt_handler() |
alert / confirm / prompt |
| 导航事件 | page.navigation |
navigationStarted、load、historyUpdated 等 |
| 通用事件 | page.events |
browsingContext / network / script / input / log 事件 |
| 网络控制 | page.network / page.intercept |
请求头、缓存控制、拦截、mock、fail、collector |
| 浏览上下文 | page.contexts |
getTree、create tab/window、reload、viewport |
| 浏览器级能力 | page.browser_tools |
user context、client window |
| 脚本能力 | page.get_realms() / page.eval_handle() / page.disown_handles() |
realms、远程对象句柄、preload script |
| Emulation | page.emulation |
UA、viewport、screen、orientation、JS 开关 |
| WebExtension | page.extensions |
安装目录扩展、安装 xpi、卸载 |
| 本地存储 | page.local_storage / page.session_storage |
读写本地存储和会话存储 |
关于 Firefox 内核与指纹浏览器
如果你的目标站点对自动化非常敏感,更推荐直接从浏览器底层方案入手。
推荐方式
优先推荐使用本项目提供的 Firefox 内核方案。
同时,ruyiPage 也可以配合任意 Firefox 指纹浏览器使用。
这类浏览器/内核的意义在于:
- 从 Firefox 底层能力上做定制
- 不是只做前端层表面伪装
- 更适合配合
ruyiPage访问风控更强的网站
结论
ruyiPage 的定位是:
- 提供 Firefox + BiDi 的高层自动化能力
- 避开 CDP 检测面
但如果你访问的是高风控站点,仍然建议:
- 优先使用本项目推荐的 Firefox 内核方案
- 再用
ruyiPage做自动化控制
这样整体效果会更稳定。
为什么是 Firefox + BiDi
现在很多自动化库都大量依赖 CDP(Chrome DevTools Protocol)。
ruyiPage 的路线不同:
- 以 Firefox 为核心浏览器
- 以 WebDriver BiDi 为核心协议
- 不依赖 CDP
这意味着:
- 没有传统 CDP 自动化暴露面
- 更贴近 W3C 新一代浏览器自动化协议方向
- 更适合做 Firefox 专项自动化、输入行为模拟、事件监听、网络控制等能力
和其他框架怎么选
下面这个表不讨论“谁绝对更强”,只突出你最关心的几个点:
- 各自主要偏向什么浏览器
- 是否依赖 CDP
- CDP 暴露面强不强
- Firefox / BiDi 支持度怎么样
- 针对性被检测情况怎么样
| 框架 | 主要浏览器方向 | 底层协议 | CDP 暴露面 | Firefox / BiDi 支持度 | 针对性被检测 |
|---|---|---|---|---|---|
ruyiPage |
Firefox | WebDriver BiDi | 无 CDP 暴露面 | 高,主路线就是 Firefox + BiDi | 低,原生 BiDi + isTrusted 行为 + 拟人操作,更适合高风控场景;配合本项目推荐的 Firefox 内核方案或任意火狐指纹浏览器会更稳定 |
| Playwright | Chromium / Firefox / WebKit | 自有协议,很多能力仍偏 Chromium | 中到高 | 中,支持 Firefox,但不是以 Firefox BiDi 为核心设计 | 中到高,很多站点会优先针对主流自动化指纹做识别 |
| Selenium | 多浏览器 | WebDriver Classic + 部分 BiDi | 低到中 | 中,兼容广,但高层 BiDi 能力不算强 | 中,传统自动化特征和使用面都比较广 |
| Puppeteer | Chromium | CDP | 高 | 低,基本不是 Firefox 主战场 | 高,CDP 路线暴露面更明显,也更容易被针对性检测 |
| DrissionPage | Chromium | 混合驱动思路,核心仍偏 Chromium | 中到高 | 低,Firefox 不是主方向 | 中到高,更偏 Chromium 自动化场景,同样容易落入主流检测面 |
一句话建议
- 你主做 Firefox 自动化:优先
ruyiPage - 你要 多浏览器统一自动化:优先 Playwright / Selenium
- 你主做 Chromium/CDP:优先 Puppeteer / Playwright
- 你想要 Firefox + 不依赖 CDP + BiDi 高层封装:
ruyiPage是更对路的选择
根目录快速开始示例
1. Bing 搜索示例
文件:quickstart_bing_search.py
它会:
- 打开 Bing
- 输入关键词
- 回车搜索
- 抓取前 3 页结果
- 打印标题、URL、摘要
核心写法:
from ruyipage import FirefoxOptions, FirefoxPage, Keys
opts = FirefoxOptions()
page = FirefoxPage(opts)
page.get("https://cn.bing.com/")
page.ele("#sb_form_q").input("小肩膀教育")
page.actions.press(Keys.ENTER).perform()
for item in page.eles("css:#b_results > li.b_algo"):
title_ele = item.ele("css:h2 a")
title = title_ele.text
url = title_ele.attr("href")
2. Cloudflare / Copilot 示例
文件:quickstart_cloudfare.py
它会:
- 打开 Copilot
- 尝试寻找输入框并发问
- 自动尝试处理 Cloudflare
- 最后打印完整 Cookie
这个示例更适合你理解:
page.handle_cloudflare_challenge()page.get_cookies(all_info=True)FirefoxOptions如何写进新手脚本
3. 指纹浏览器示例
文件:quickstart_fingerprint_browser.py
它会:
- 启动 Firefox 指纹浏览器
- 通过
--fpfile=...加载指纹文件 - 打开
browserscan检查指纹结果 - 叠加地理位置、时区、语言、请求头、屏幕尺寸模拟
核心写法:
from ruyipage import FirefoxOptions, FirefoxPage
opts = FirefoxOptions()
opts.set_browser_path(r"C:\Program Files\Mozilla Firefox\firefox.exe")
opts.set_fpfile(r"C:\fingerprints\profile1.txt")
page = FirefoxPage(opts)
page.get("https://www.browserscan.net/zh")
page.emulation.set_geolocation(39.9042, 116.4074, accuracy=100)
page.emulation.set_timezone("Asia/Tokyo")
page.emulation.set_locale(["ja-JP", "ja"])
page.network.set_extra_headers({
"Accept-Language": "ja-JP,ja;q=0.9"
})
page.emulation.set_screen_size(1366, 768, device_pixel_ratio=2.0)
page.refresh()
适用场景:
- 需要把 Firefox 指纹浏览器和
ruyiPage配合使用 - 希望把指纹文件、语言、请求头、屏幕参数一起带上
- 想直接验证
browserscan等站点上的指纹表现
4. 接管已打开的 Firefox 指纹浏览器示例
文件:examples/39_attach_exist_browser.py
它会:
- 提示你先手工启动 Firefox 或 Firefox 指纹浏览器
- 直接按 Firefox / ADS / FlowerBrowser 进程特征自动探测并接管
- 接管成功后直接操作当前浏览器实例
核心写法:
from ruyipage import auto_attach_exist_browser_by_process
page = auto_attach_exist_browser_by_process(
latest_tab=True,
)
print(page.browser.address)
print(page.title)
print(page.url)
适用场景:
- 已经打开了 ADS / FlowerBrowser 之类的 Firefox 指纹浏览器
- 浏览器后台会把
--remote-debugging-port=9222改成随机端口 - 想直接按进程特征自动探测真实端口并接管已有实例
5. HTTP 密码代理示例
如果你使用的是本项目自己的 Firefox 内核,那么内核已经支持从 fpfile 自动读取 HTTP 代理用户名密码。
也就是说,业务层只需要:
opts.set_proxy("http://host:port")opts.set_fpfile("...")
当 fpfile 中存在以下字段时,内核会自动处理 HTTP 代理认证,不需要再额外调用认证 API:
httpauth.username:your-proxy-username
httpauth.password:your-proxy-password
完整示例见:examples/38_proxy_auth_ipinfo.py
核心写法:
from ruyipage import FirefoxOptions, FirefoxPage
opts = FirefoxOptions()
opts.set_proxy("http://your-proxy-host:8080")
opts.set_fpfile(r"C:\path\to\your\profile1.txt")
page = FirefoxPage(opts)
page.get("http://ipinfo.io/json")
适用场景:
- 你在自己的 Firefox 内核里已经实现了
fpfile驱动的 HTTP 代理认证 - 希望业务层只保留最小代理配置入口
- 想让代理用户名密码完全留在
fpfile中,而不是写进业务脚本
最常用 API 文档
下面不是底层 BiDi 命令表,而是 新手最常直接用到的高层 API。
阅读建议:
- 先看
FirefoxPage- 这是最核心的页面对象,绝大多数操作都从这里开始。
- 再看
ele()/eles()- 元素定位是最常用的基础能力。
- 再看
actions / downloads / network / events- 这些是自动化中最常扩展的高级能力。
文档风格说明:
- 这里优先写 最常用、最实用 的高层接口。
- 不会把底层 BiDi 命令原样堆出来让新手自己拼参数。
- 每个能力尽量说明:
- 它是做什么的
- 什么时候该用
- 最常见的写法是什么
- 返回值你能继续怎么用
1. 页面对象:FirefoxPage
创建页面
from ruyipage import FirefoxPage, FirefoxOptions
page = FirefoxPage()
opts = FirefoxOptions()
page = FirefoxPage(opts)
常用属性
| API | 说明 | 返回值 |
|---|---|---|
page.title |
当前页面标题 | str |
page.url |
当前页面 URL | str |
page.html |
当前页面 HTML | str |
page.tab_id |
当前 tab 的 browsingContext ID | str |
page.cookies |
当前页面可见 Cookie 列表 | list[CookieInfo] |
常用导航
page.get("https://www.example.com")
page.refresh()
page.back()
page.forward()
page.quit()
page.get(url, wait='complete')
打开一个页面。
page.get("https://www.example.com")
page.get("https://www.example.com", wait="interactive")
参数说明:
url- 要访问的地址
- 常见值:
https://...、file:///...、data:text/html,...
wait- 页面等待策略
- 常见值:
complete:等页面完全加载interactive:等 DOMContentLoadednone:发出导航后立即返回
适用场景:
- 日常页面打开:用默认
complete - 页面很慢但你只想先拿 DOM:用
interactive - 你后面会自己监听事件或手动等:用
none
page.back() / page.forward() / page.refresh()
这些分别用于:
- 后退
- 前进
- 刷新
page.back()
page.forward()
page.refresh()
如果你需要验证导航事件,建议和 page.navigation 配合使用。
2. 元素查找:ele() / eles()
page.ele(locator)
查找一个元素。
最常用写法:
page.ele("#kw")
page.ele("css:.item")
page.ele("css:div.card > a")
page.ele("xpath://button[text()='登录']")
page.ele("tag:input")
page.ele("text:登录")
新手建议优先顺序:
#idcss:...xpath:...
page.eles(locator)
查找所有匹配元素。
items = page.eles("css:.card")
rows = page.eles("css:table tbody tr")
links = page.eles("tag:a")
在元素内部继续查找
card = page.ele("css:.card")
title = card.ele("css:h2 a")
desc = card.ele("css:.desc")
常用元素 API
| API | 说明 | 返回值 |
|---|---|---|
ele.text |
元素文本 | str |
ele.html |
outerHTML | str |
ele.value |
表单值 | `str |
ele.attr("href") |
属性值 | str |
ele.click_self() |
直接点击元素 | self |
ele.input("abc") |
输入文本 | self |
ele.clear() |
清空内容 | self |
ele.hover() |
鼠标悬停 | self |
ele.drag_to(target) |
拖到目标 | self |
ele.text
读取元素文本。
title = page.ele("css:h1").text
适合读取:
- 标题
- 按钮文案
- 搜索结果摘要
ele.attr(name)
读取元素属性。
url = page.ele("css:a").attr("href")
src = page.ele("css:img").attr("src")
常见属性:
hrefsrcvalueplaceholderclassid
ele.click_self()
直接点击元素。
page.ele("text:登录").click_self()
这是最推荐新手使用的点击方法。
ele.input(text, clear=True)
给输入框输入内容。
page.ele("#kw").input("ruyiPage")
page.ele("#kw").input("ruyiPage", clear=True)
适用场景:
- 文本输入
- 搜索框输入
- 文件输入框上传文件
如果元素本身是 <input type="file">,传文件路径即可:
page.ele("#upload").input(r"D:\test.txt")
page.ele("#upload").input([r"D:\1.txt", r"D:\2.txt"])
3. 动作链:page.actions
用于原生 BiDi 输入动作。
page.actions.press(Keys.ENTER).perform()
page.actions.move_to(page.ele("#btn")).click().perform()
page.actions.drag(page.ele("#a"), page.ele("#b")).perform()
page.actions.release()
常见用途:
- 键盘输入
- 鼠标点击
- 拖拽
- 滚轮滚动
- 拟人化移动和点击
常见写法
回车
from ruyipage import Keys
page.actions.press(Keys.ENTER).perform()
点击某个元素
page.actions.move_to(page.ele("#btn")).click().perform()
拖拽
page.actions.drag(page.ele("#source"), page.ele("#target")).perform()
page.actions.release()
为什么推荐 page.actions
因为这条链更接近原生 BiDi 输入模型,很多动作事件能保持更真实的浏览器输入行为。
4. Cookies
获取 Cookie
cookies = page.get_cookies()
for cookie in cookies:
print(cookie.name, cookie.value)
返回对象通常是 CookieInfo,常用字段:
cookie.namecookie.valuecookie.domaincookie.pathcookie.http_onlycookie.securecookie.same_sitecookie.expiry
按条件过滤 Cookie
cookies = page.get_cookies_filtered(name="session_id", all_info=True)
设置 Cookie
page.set_cookies({
"name": "token",
"value": "abc",
"domain": "127.0.0.1",
"path": "/",
})
也可以一次传多个:
page.set_cookies([
{"name": "a", "value": "1", "domain": "127.0.0.1", "path": "/"},
{"name": "b", "value": "2", "domain": "127.0.0.1", "path": "/"},
])
删除 Cookie
page.delete_cookies(name="token")
page.delete_cookies()
5. 下载
高层入口:page.downloads
page.downloads.set_behavior("allow", path=r"D:\downloads")
page.downloads.start()
page.ele("#download").click_self()
event = page.downloads.wait("browsingContext.downloadEnd", timeout=5)
print(event.status)
常用方法:
set_behavior()set_path()start()stop()wait()wait_chain()wait_file()
典型下载流程
page.downloads.set_behavior("allow", path=r"D:\downloads")
page.downloads.start()
page.ele("#download").click_self()
begin = page.downloads.wait("browsingContext.downloadWillBegin", timeout=5)
end = page.downloads.wait("browsingContext.downloadEnd", timeout=5)
print(begin.suggested_filename)
print(end.status)
6. 导航事件
高层入口:page.navigation
page.navigation.start()
page.get("https://www.example.com")
event = page.navigation.wait("browsingContext.load", timeout=5)
print(event.url)
page.navigation.stop()
适合验证:
navigationStarteddomContentLoadedloadhistoryUpdatednavigationCommitted
7. 通用事件监听
高层入口:page.events
page.events.start(["network.beforeRequestSent"], contexts=[page.tab_id])
event = page.events.wait("network.beforeRequestSent", timeout=5)
page.events.stop()
适合统一承接:
browsingContext.*network.*script.*input.*log.*
返回对象:BidiEvent
常用字段:
methodcontexturlrequestresponseerror_textchanneldatamultiplemessage
什么时候用 page.events
当你想直接监听协议事件,而不是只关心页面最终状态时,用它最合适。
例如:
- 监听
network.beforeRequestSent - 监听
browsingContext.contextCreated - 监听
script.message - 监听
input.fileDialogOpened
8. 网络能力
高层入口:page.intercept(拦截)、page.listen(监听)、page.network(配置)
请求拦截
拦截请求阶段(beforeRequestSent),可修改、Mock 或阻止请求:
# 回调模式:拦截并 Mock 响应
def handler(req):
if '/api/data' in req.url:
req.mock(
'{"status":"ok","data":"mocked"}',
headers={"content-type": "application/json",
"access-control-allow-origin": "*"},
)
else:
req.continue_request()
page.intercept.start_requests(handler)
page.get("https://example.com")
page.intercept.stop()
# 修改请求头(headers 支持 dict 简洁格式)
def handler(req):
req.continue_request(headers={
"X-Token": "abc123",
"User-Agent": "RuyiPage/1.0",
})
page.intercept.start_requests(handler)
# 阻止请求
def handler(req):
if req.url.endswith(('.png', '.jpg', '.gif')):
req.fail()
else:
req.continue_request()
page.intercept.start_requests(handler)
# 队列模式:手动处理
page.intercept.start_requests()
# ... 触发网络请求 ...
req = page.intercept.wait(timeout=5)
print(req.method, req.url, req.body)
req.continue_request()
page.intercept.stop()
响应拦截
拦截响应阶段(responseStarted),可读取、修改响应信息:
# 读取原始响应状态码和头
def handler(req):
print(f"状态码: {req.response_status}")
print(f"Content-Type: {req.response_headers.get('content-type')}")
req.continue_response()
page.intercept.start_responses(handler)
# 修改响应状态码
def handler(req):
if '/api' in req.url:
req.continue_response(status_code=200, reason_phrase="OK")
else:
req.continue_response()
page.intercept.start_responses(handler)
一步读取响应体
启用 collect_response=True 后,可通过 req.response_body 一步读取响应体,无需手动编排 DataCollector:
page.intercept.start_requests(collect_response=True)
# ... 触发网络请求 ...
req = page.intercept.wait(timeout=5)
req.continue_request()
body = req.response_body # 自动等待响应完成 + 解码
print(body)
page.intercept.stop() # 自动清理内部 collector
设置额外请求头
page.network.set_extra_headers({"X-Test": "yes"})
这通常用于:
- 给接口加测试请求头
- 做环境标记
- 配合拦截验证请求头是否真的发出
设置缓存行为
page.network.set_cache_behavior("bypass")
其中:
default: 浏览器默认缓存策略,命中缓存时可能不再发真实请求bypass: 尽量绕过缓存,强制重新请求资源
Data Collector
collector = page.network.add_data_collector(
["responseCompleted"],
data_types=["response"],
)
data = collector.get(request_id, data_type="response")
collector.disown(request_id, data_type="response")
collector.remove()
其中:
eventsbeforeRequestSent:在请求发出阶段采集responseCompleted:在响应完成阶段采集
data_typesrequest:收集请求体response:收集响应体
9. 浏览上下文
高层入口:page.contexts
tree = page.contexts.get_tree()
print(len(tree.contexts))
tab_id = page.contexts.create_tab()
page.contexts.close(tab_id)
page.contexts.reload()
page.contexts.set_viewport(800, 600)
常用方法:
get_tree()create_tab()create_window()close()reload()set_viewport()set_bypass_csp()
tree = page.contexts.get_tree()
返回的不是裸 dict,而是高层结果对象。
tree = page.contexts.get_tree()
print(len(tree.contexts))
first = tree.contexts[0]
print(first.context)
print(first.url)
10. 浏览器级能力
高层入口:page.browser_tools
user_context = page.browser_tools.create_user_context()
contexts = page.browser_tools.get_user_contexts()
page.browser_tools.remove_user_context(user_context)
windows = page.browser_tools.get_client_windows()
page.browser_tools.set_window_state(windows[0]["clientWindow"], state="maximized")
适合做:
- user context 管理
- client window 管理
典型用法
ctx = page.browser_tools.create_user_context()
tab_id = page.browser_tools.create_tab(user_context=ctx)
page.contexts.close(tab_id)
page.browser_tools.remove_user_context(ctx)
11. Script 能力
获取 realms
realms = page.get_realms()
for realm in realms:
print(realm.type, realm.context)
执行脚本并拿 handle
result = page.eval_handle("({a: 1, b: 2})")
print(result.success)
print(result.result.handle)
page.disown_handles([result.result.handle])
这个流程适合:
- 需要拿远程 JS 对象句柄
- 用完后再手动释放 handle
preload script
preload = page.add_preload_script("""
() => {
window.__ready = 'ok';
}
""")
page.get("https://www.example.com")
print(page.run_js("return window.__ready"))
page.remove_preload_script(preload)
适用场景:
- 在页面脚本执行前先注入一段初始化逻辑
- 给页面预先挂钩子、打标记、注入辅助函数
12. 弹窗
高层入口:
page.wait_prompt()page.accept_prompt()page.dismiss_prompt()page.input_prompt(text)page.set_prompt_handler(...)page.clear_prompt_handler()
典型写法
等待后手动处理
page.run_js("alert('hello')", as_expr=False)
prompt = page.wait_prompt(timeout=3)
page.accept_prompt()
自动处理 prompt
page.set_prompt_handler(prompt="ignore", prompt_text="张三")
page.run_js("prompt('请输入姓名')", as_expr=False)
page.clear_prompt_handler()
page.set_prompt_handler(prompt="ignore", prompt_text="张三")
page.run_js("prompt('请输入姓名')", as_expr=False)
page.clear_prompt_handler()
13. Emulation
高层入口:page.emulation
page.emulation.set_network_offline(True)
page.emulation.set_javascript_enabled(False)
page.emulation.set_scrollbar_type("overlay")
page.emulation.apply_mobile_preset(
width=390,
height=844,
device_pixel_ratio=3,
user_agent="...",
)
注意:
- 某些 emulation 命令在当前 Firefox 版本中可能未实现
- 示例里会区分“成功”和“不支持”
典型用法
page.emulation.apply_mobile_preset(
width=390,
height=844,
device_pixel_ratio=3,
user_agent="Mozilla/5.0 ...",
)
14. WebExtension
高层入口:page.extensions
ext_id = page.extensions.install_dir(r"D:\my_extension")
page.extensions.uninstall(ext_id)
适用场景:
- 验证 content script 是否生效
- 测试目录扩展和 xpi 安装流程
15. 代表性示例
仓库里已经包含大量示例,建议按编号学习。
推荐顺序:
入门
01_basic_navigation.py02_element_finding.py03_element_interaction.py05_actions_chain.py06_screenshot.py
页面与脚本
07_javascript.py08_cookies.py09_tabs.py13_iframe.py14_shadow_dom.py
高级能力
17_user_prompts.py18_advanced_network.py19_pdf_printing.py20_advanced_input.py21_emulation.py
严格结果版示例
23_download.py24_navigation_events.py25_browser_user_context.py37_three_isolated_user_context_tabs.py单浏览器多 tab 使用不同 user context,实现 Cookie 隔离26_browsing_context_advanced.py27_emulation_advanced.py28_network_data_collector.py29_script_input_advanced.py30_browsing_context_events.py31_network_events.py32_script_events.py33_log_input_events.py34_remaining_commands.py35_native_bidi_drag.py36_native_bidi_select.py39_attach_exist_browser.py自动探测可接管实例,再接管已打开的 Firefox/指纹浏览器42_xpath_picker_complex_showcase.py启动 XPath picker,并打开包含复杂节点、shadow root、嵌套 iframe 的综合展示页
协议来源
ruyiPage 的底层核心能力对照并基于:
- WebDriver BiDi: https://w3c.github.io/webdriver-bidi/
这也是本项目很多高层 API 的设计来源,例如:
browsingContext.*network.*script.*input.*browser.*emulation.*
Star History
最后说明
ruyiPage 的核心方向不是“把所有底层 BiDi 命令原样裸露给用户”,而是:
- 对 Firefox 自动化足够友好
- 对新手足够直观
- 对编辑器跳转足够友好
- 对高级用户保留足够的 BiDi 能力空间
如果你主要使用 Firefox 做自动化,并且希望尽量避开 CDP 检测面,
同时又想要比直接写底层协议更好理解的 API,ruyiPage 就是为这个方向准备的。
使用声明与免责声明
本项目仅用于:
- 探索下一代自动化框架
- 学习 Firefox 自动化能力
- 学习 WebDriver BiDi 协议
- 学习浏览器自动化高层 API 设计
- 合法、合规、非盈利的个人研究与技术交流
授权范围
允许任何人以个人身份使用或分发本项目源代码,但仅限于:
- 学习目的
- 技术研究目的
- 合法、合规、非盈利目的
个人或组织如未获得版权持有人授权,不得将本项目以源代码或二进制形式用于商业行为。
使用条款
使用本项目需满足以下条款,如使用过程中出现违反任意一项条款的情形,授权自动失效。
- 禁止将
ruyiPage应用于任何可能违反当地法律规定和道德约束的项目中 - 禁止将
ruyiPage用于任何可能有损他人利益的项目中 - 禁止将
ruyiPage用于攻击、骚扰、批量滥用、恶意注册、撞库、刷量等行为 - 禁止将
ruyiPage用于规避平台安全机制后实施违法行为 - 使用者应遵守目标网站或系统的 Robots、服务条款及当地法律法规
- 禁止将
ruyiPage用于采集法律、条款或 Robots 协议明确不允许的数据
风险与责任
使用 ruyiPage 发生的一切行为,均由使用人自行负责。
因使用 ruyiPage 进行任何行为所产生的一切纠纷及后果,均与版权持有人无关。
版权持有人不承担任何因使用 ruyiPage 带来的风险、损失、封号、限制、数据问题、法律后果或间接损失。
版权持有人也不对 ruyiPage 可能存在的缺陷、兼容性问题、误操作风险或目标网站策略变化导致的任何损失承担责任。
特别说明
本项目强调:
- Firefox 自动化
- BiDi 协议能力
isTrusted行为- 拟人化行为能力
- 高风控场景适配
但这些能力仅限于合法、合规、正当的技术研究和自动化应用场景。
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ruyipage-1.1.4.tar.gz.
File metadata
- Download URL: ruyipage-1.1.4.tar.gz
- Upload date:
- Size: 270.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
457ec6ed54b6d146abfba044edde9cb8f0d39974eb58485f84afba699d93b710
|
|
| MD5 |
f7da401fd70ef22b293de61342005287
|
|
| BLAKE2b-256 |
a71662ffab2a831befe8b395e4d0c8ae26ee2c831e566b8cd0edc88c75d73603
|
File details
Details for the file ruyipage-1.1.4-py3-none-any.whl.
File metadata
- Download URL: ruyipage-1.1.4-py3-none-any.whl
- Upload date:
- Size: 214.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7c13675af20083ae0def73fecde8caa170ce8c8bce074d77b08bb6a528aa92a7
|
|
| MD5 |
cc4f023bc0069f1f50c1543460c9feae
|
|
| BLAKE2b-256 |
c5dab5a10602c4bbc4c7de718e6dc6df940cf729853e23320f86685c8e89f2f3
|