演示如何把 Firefox 指纹浏览器、指纹文件、地理位置、语言和请求头一起接入 RuyiPage。
- 加载 Firefox 指纹浏览器可执行文件
- 通过 fpfile 复用指纹配置
- 叠加地理位置、时区和语言环境
- 为请求增加语言请求头
- 访问指纹检测站点验证效果
适合高风控、跨地区语言环境模拟和指纹浏览器接入文档。
这里不是简单的文件清单,而是将 RuyiPage 仓库中的 examples 渲染成案例卡。每个例子都带有文件名、分类、难度、不同用法、涉及 API、适用场景与相关页面,并提供 GitHub 源码入口,帮助你从“例子”回到“文档”,也从“文档”回到“例子”。
演示如何把 Firefox 指纹浏览器、指纹文件、地理位置、语言和请求头一起接入 RuyiPage。
适合高风控、跨地区语言环境模拟和指纹浏览器接入文档。
展示 Firefox 私密浏览模式的两种启动方式,以及它与临时 profile 的区别。
适合新手理解浏览器启动模式、临时会话和隐私场景。
覆盖最常用的页面与元素截图输出能力。
适合 screenshots 页面和综合示例的关联阅读。
来源:app/content/ruyipage_examples/06_screenshot.py · 仓库文件:06_screenshot.py
# =============================
# RuyiPage 示例注释版
# 标题: 截图功能:整页、元素、base64、bytes
# 说明: 覆盖最常用的页面与元素截图输出能力。
# 你会学到:
# 1. 整页截图并保存到文件
# 2. 对单个元素或区域截图
# 3. 滚动到目标后截图
# 4. 获取截图的 base64 和 bytes 数据
# 相关 API: page.screenshot, ele.screenshot, page.scroll.to_see, page.scroll.to_bottom
# =============================
# -*- coding: utf-8 -*-
"""
示例6: 截图功能
测试功能:
- 整页截图
- 元素截图
- 保存为文件
- 获取base64
- 获取bytes
"""
import os
import sys
import io
# 设置控制台输出编码为UTF-8(Windows兼容)
if sys.platform == 'win32':
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from ruyipage import FirefoxPage, FirefoxOptions
def test_screenshot():
"""测试截图功能"""
print("=" * 60)
print("测试6: 截图功能")
print("=" * 60)
opts = FirefoxOptions()
opts.headless(False)
page = FirefoxPage(opts)
try:
# 加载测试页面
test_page = os.path.join(os.path.dirname(__file__), 'test_pages', 'test_page.html')
test_url = 'file:///' + os.path.abspath(test_page).replace('\\', '/')
page.get(test_url)
page.wait(1)
# 创建输出目录
output_dir = os.path.join(os.path.dirname(__file__), 'output', 'screenshots')
os.makedirs(output_dir, exist_ok=True)
# 1. 整页截图
print("\n1. 整页截图:")
full_path = os.path.join(output_dir, 'full_page.png')
page.screenshot(full_path)
print(f" ✓ 整页截图已保存: {full_path}")
# 2. 元素截图
print("\n2. 元素截图:")
title = page.ele('#main-title')
title_path = os.path.join(output_dir, 'title_element.png')
title.screenshot(title_path)
print(f" ✓ 标题元素截图已保存: {title_path}")
# 3. 表单区域截图
print("\n3. 表单区域截图:")
form_section = page.ele('#form-section')
form_path = os.path.join(output_dir, 'form_section.png')
form_section.screenshot(form_path)
print(f" ✓ 表单区域截图已保存: {form_path}")
# 4. 按钮截图
print("\n4. 按钮截图:")
btn = page.ele('#click-btn')
# 先滚动到按钮位置确保可见
page.scroll.to_see(btn)
page.wait(0.5)
btn_path = os.path.join(output_dir, 'button.png')
try:
btn.screenshot(btn_path)
print(f" ✓ 按钮截图已保存: {btn_path}")
except Exception as e:
print(f" ⚠ 按钮截图跳过(元素尺寸问题): {str(e)[:50]}")
# 5. 获取截图的base64
print("\n5. 获取截图base64:")
base64_data = page.screenshot(as_base64=True)
print(f" ✓ Base64数据长度: {len(base64_data)} 字符")
# 6. 获取截图的bytes
print("\n6. 获取截图bytes:")
bytes_data = page.screenshot(as_bytes=True)
print(f" ✓ Bytes数据长度: {len(bytes_data)} 字节")
# 7. 元素base64截图
print("\n7. 元素base64截图:")
try:
# 使用表单区域元素,它更稳定
form_elem = page.ele('#form-section')
elem_base64 = form_elem.screenshot(as_base64=True)
print(f" ✓ 元素Base64数据长度: {len(elem_base64)} 字符")
except Exception as e:
print(f" ⚠ 元素base64截图跳过(元素尺寸问题): {str(e)[:50]}")
# 8. 滚动后截图
print("\n8. 滚动后截图:")
page.scroll.to_bottom()
page.wait(0.5)
bottom_path = os.path.join(output_dir, 'page_bottom.png')
page.screenshot(bottom_path)
print(f" ✓ 页面底部截图已保存: {bottom_path}")
# 9. 表格截图
print("\n9. 表格截图:")
table = page.ele('#data-table')
table_path = os.path.join(output_dir, 'table.png')
try:
page.scroll.to_see(table)
page.wait(0.5)
table.screenshot(table_path)
print(f" ✓ 表格截图已保存: {table_path}")
except Exception as e:
print(f" ⚠ 表格截图跳过(元素尺寸问题): {str(e)[:50]}")
print("\n" + "=" * 60)
print("✓ 所有截图测试通过!")
print(f"截图保存在: {output_dir}")
print("=" * 60)
except Exception as e:
print(f"\n✗ 测试失败: {e}")
import traceback
traceback.print_exc()
finally:
page.wait(2)
page.quit()
if __name__ == '__main__':
test_screenshot()
# -*- coding: utf-8 -*-
"""
示例6: 截图功能
测试功能:
- 整页截图
- 元素截图
- 保存为文件
- 获取base64
- 获取bytes
"""
import os
import sys
import io
# 设置控制台输出编码为UTF-8(Windows兼容)
if sys.platform == 'win32':
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from ruyipage import FirefoxPage, FirefoxOptions
def test_screenshot():
"""测试截图功能"""
print("=" * 60)
print("测试6: 截图功能")
print("=" * 60)
opts = FirefoxOptions()
opts.headless(False)
page = FirefoxPage(opts)
try:
# 加载测试页面
test_page = os.path.join(os.path.dirname(__file__), 'test_pages', 'test_page.html')
test_url = 'file:///' + os.path.abspath(test_page).replace('\\', '/')
page.get(test_url)
page.wait(1)
# 创建输出目录
output_dir = os.path.join(os.path.dirname(__file__), 'output', 'screenshots')
os.makedirs(output_dir, exist_ok=True)
# 1. 整页截图
print("\n1. 整页截图:")
full_path = os.path.join(output_dir, 'full_page.png')
page.screenshot(full_path)
print(f" ✓ 整页截图已保存: {full_path}")
# 2. 元素截图
print("\n2. 元素截图:")
title = page.ele('#main-title')
title_path = os.path.join(output_dir, 'title_element.png')
title.screenshot(title_path)
print(f" ✓ 标题元素截图已保存: {title_path}")
# 3. 表单区域截图
print("\n3. 表单区域截图:")
form_section = page.ele('#form-section')
form_path = os.path.join(output_dir, 'form_section.png')
form_section.screenshot(form_path)
print(f" ✓ 表单区域截图已保存: {form_path}")
# 4. 按钮截图
print("\n4. 按钮截图:")
btn = page.ele('#click-btn')
# 先滚动到按钮位置确保可见
page.scroll.to_see(btn)
page.wait(0.5)
btn_path = os.path.join(output_dir, 'button.png')
try:
btn.screenshot(btn_path)
print(f" ✓ 按钮截图已保存: {btn_path}")
except Exception as e:
print(f" ⚠ 按钮截图跳过(元素尺寸问题): {str(e)[:50]}")
# 5. 获取截图的base64
print("\n5. 获取截图base64:")
base64_data = page.screenshot(as_base64=True)
print(f" ✓ Base64数据长度: {len(base64_data)} 字符")
# 6. 获取截图的bytes
print("\n6. 获取截图bytes:")
bytes_data = page.screenshot(as_bytes=True)
print(f" ✓ Bytes数据长度: {len(bytes_data)} 字节")
# 7. 元素base64截图
print("\n7. 元素base64截图:")
try:
# 使用表单区域元素,它更稳定
form_elem = page.ele('#form-section')
elem_base64 = form_elem.screenshot(as_base64=True)
print(f" ✓ 元素Base64数据长度: {len(elem_base64)} 字符")
except Exception as e:
print(f" ⚠ 元素base64截图跳过(元素尺寸问题): {str(e)[:50]}")
# 8. 滚动后截图
print("\n8. 滚动后截图:")
page.scroll.to_bottom()
page.wait(0.5)
bottom_path = os.path.join(output_dir, 'page_bottom.png')
page.screenshot(bottom_path)
print(f" ✓ 页面底部截图已保存: {bottom_path}")
# 9. 表格截图
print("\n9. 表格截图:")
table = page.ele('#data-table')
table_path = os.path.join(output_dir, 'table.png')
try:
page.scroll.to_see(table)
page.wait(0.5)
table.screenshot(table_path)
print(f" ✓ 表格截图已保存: {table_path}")
except Exception as e:
print(f" ⚠ 表格截图跳过(元素尺寸问题): {str(e)[:50]}")
print("\n" + "=" * 60)
print("✓ 所有截图测试通过!")
print(f"截图保存在: {output_dir}")
print("=" * 60)
except Exception as e:
print(f"\n✗ 测试失败: {e}")
import traceback
traceback.print_exc()
finally:
page.wait(2)
page.quit()
if __name__ == '__main__':
test_screenshot()
围绕登录态和会话管理的核心示例。
适合 cookies 与 browser 页面。
来源:app/content/ruyipage_examples/08_cookies.py · 仓库文件:08_cookies.py
# =============================
# RuyiPage 示例注释版
# 标题: Cookie 管理:读取、设置、过滤、删除
# 说明: 围绕登录态和会话管理的核心示例。
# 你会学到:
# 1. 读取服务端下发 Cookie
# 2. 通过 API 设置 Cookie
# 3. 按 name 等条件过滤 Cookie
# 4. 删除指定 Cookie 或清空全部 Cookie
# 5. 读取浏览器级 Cookie 视图
# 相关 API: page.get_cookies, page.set_cookies, page.get_cookies_filtered, page.delete_cookies, page.browser.cookies
# =============================
# -*- coding: utf-8 -*-
"""
示例8: Cookie管理
测试功能:
- 获取Cookie
- 设置Cookie
- 删除Cookie
- 清空所有Cookie
"""
import os
import sys
import io
import time
# 设置控制台输出编码为UTF-8(Windows兼容)
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
sys.path.insert(0, os.path.dirname(__file__))
from ruyipage import FirefoxPage, FirefoxOptions
from test_server import TestServer
def test_cookies():
"""测试Cookie管理功能"""
print("=" * 60)
print("测试8: Cookie管理")
print("=" * 60)
# 启动测试服务器
server = TestServer(port=8888)
server.start()
opts = FirefoxOptions()
opts.headless(False)
page = FirefoxPage(opts)
try:
# 访问测试服务器的Cookie设置页面
print("\n访问测试服务器...")
page.get(server.get_url("/set-cookie"))
page.wait(2)
# 1. 获取服务器设置的Cookie
print("\n1. 获取服务器设置的Cookie:")
all_cookies = page.get_cookies(all_info=True)
print(f" 共有 {len(all_cookies)} 个Cookie")
for cookie in all_cookies:
name = cookie.name
value = cookie.value
print(f" - {name}: {value}")
# 2. 通过API设置Cookie
print("\n2. 通过API设置Cookie:")
page.set_cookies(
{
"name": "api_cookie",
"value": "api_value",
"domain": "127.0.0.1",
"path": "/",
}
)
page.set_cookies(
{"name": "user_id", "value": "12345", "domain": "127.0.0.1", "path": "/"}
)
print(f" ✓ 已通过API设置2个Cookie")
# 3. 验证Cookie已设置
print("\n3. 验证Cookie已设置:")
page.refresh()
page.wait(1)
all_cookies = page.get_cookies(all_info=True)
print(f" 刷新后共有 {len(all_cookies)} 个Cookie")
# 转换为字典方便查找
cookies_dict = {}
for c in all_cookies:
cookies_dict[c.name] = c.value
if "api_cookie" in cookies_dict:
print(f" ✓ api_cookie = {cookies_dict['api_cookie']}")
if "user_id" in cookies_dict:
print(f" ✓ user_id = {cookies_dict['user_id']}")
# 4. 设置带过期时间的Cookie
print("\n4. 设置带过期时间的Cookie:")
expire_time = int(time.time()) + 3600 # 1小时后过期
page.set_cookies(
{
"name": "expire_cookie",
"value": "will_expire",
"domain": "127.0.0.1",
"path": "/",
"expiry": expire_time,
}
)
print(f" ✓ 已设置带过期时间的Cookie(1小时后过期)")
# 5. 设置HttpOnly和Secure Cookie
print("\n5. 设置HttpOnly和Secure Cookie:")
page.set_cookies(
{
"name": "secure_cookie",
"value": "secure_value",
"domain": "127.0.0.1",
"path": "/",
"httpOnly": True,
"secure": False, # HTTP不能用secure=True
}
)
print(f" ✓ 已设置HttpOnly Cookie")
# 6. 获取特定Cookie
print("\n6. 获取特定Cookie:")
all_cookies = page.get_cookies(all_info=True)
cookies_dict = {}
for c in all_cookies:
cookies_dict[c.name] = c.value
if "user_id" in cookies_dict:
print(f" user_id = {cookies_dict['user_id']}")
if "expire_cookie" in cookies_dict:
print(f" expire_cookie = {cookies_dict['expire_cookie']}")
# 6.1 简单 cookies 属性
print("\n6.1 当前页面 cookies 属性:")
simple_cookies = page.cookies
print(f" page.cookies 数量: {len(simple_cookies)}")
# 6.2 setter 风格 Cookie API
print("\n6.2 通过 page.set.cookies 设置Cookie:")
page.set.cookies(
{
"name": "setter_cookie",
"value": "setter_value",
"domain": "127.0.0.1",
"path": "/",
}
)
page.wait(0.5)
setter_cookies = {c.name: c.value for c in page.get_cookies()}
print(f" setter_cookie = {setter_cookies.get('setter_cookie')}")
# 6.3 通过高层过滤 API 读取 Cookie
print("\n6.3 通过高层过滤 API 读取Cookie:")
filtered_cookies = page.get_cookies_filtered(name="user_id", all_info=True)
print(f" 过滤结果数量: {len(filtered_cookies)}")
if filtered_cookies:
value = filtered_cookies[0].value
print(f" user_id(filtered) = {value}")
# 7. 删除特定Cookie
print("\n7. 删除特定Cookie:")
page.delete_cookies(name="api_cookie")
print(f" ✓ 已删除 api_cookie")
# 验证删除
page.wait(0.5)
all_cookies = page.get_cookies(all_info=True)
cookies_dict = {}
for c in all_cookies:
cookies_dict[c.name] = c.value
if "api_cookie" not in cookies_dict:
print(f" ✓ 确认 api_cookie 已被删除")
else:
print(f" ⚠ api_cookie 仍然存在")
# 7.1 通过公开删除接口删除 setter_cookie
print("\n7.1 删除 setter_cookie:")
page.delete_cookies(name="setter_cookie", domain="127.0.0.1")
page.wait(0.5)
cookies_after_remove = {c.name: c.value for c in page.get_cookies()}
print(f" setter_cookie 是否存在: {'setter_cookie' in cookies_after_remove}")
# 8. 访问API验证Cookie发送
print("\n8. 访问API验证Cookie发送:")
page.get(server.get_url("/get-cookie"))
page.wait(1)
body_text = page.ele("tag:body").text
print(f" 服务器收到的Cookie: {body_text[:100]}")
# 9. 清空所有Cookie
print("\n9. 清空所有Cookie:")
page.delete_cookies()
print(f" ✓ 已清空所有Cookie")
# 验证清空
all_cookies = page.get_cookies()
print(f" 清空后Cookie数量: {len(all_cookies)}")
# 9.1 浏览器级 Cookie 读取
print("\n9.1 浏览器级 cookies 读取:")
browser_cookies = page.browser.cookies(all_info=False)
print(f" browser.cookies 数量: {len(browser_cookies)}")
# 10. 重新设置并测试
print("\n10. 重新设置Cookie并测试:")
page.set_cookies(
{
"name": "final_cookie",
"value": "final_value",
"domain": "127.0.0.1",
"path": "/",
}
)
page.refresh()
page.wait(1)
all_cookies = page.get_cookies(all_info=True)
cookies_dict = {}
for c in all_cookies:
cookies_dict[c.name] = c.value
if "final_cookie" in cookies_dict:
print(f" ✓ final_cookie = {cookies_dict['final_cookie']}")
print(f" ✓ Cookie持久性测试通过")
print("\n" + "=" * 60)
print("✓ 所有Cookie管理测试通过!")
print("=" * 60)
except Exception as e:
print(f"\n✗ 测试失败: {e}")
import traceback
traceback.print_exc()
finally:
page.wait(2)
page.quit()
server.stop()
if __name__ == "__main__":
test_cookies()
# -*- coding: utf-8 -*-
"""
示例8: Cookie管理
测试功能:
- 获取Cookie
- 设置Cookie
- 删除Cookie
- 清空所有Cookie
"""
import os
import sys
import io
import time
# 设置控制台输出编码为UTF-8(Windows兼容)
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
sys.path.insert(0, os.path.dirname(__file__))
from ruyipage import FirefoxPage, FirefoxOptions
from test_server import TestServer
def test_cookies():
"""测试Cookie管理功能"""
print("=" * 60)
print("测试8: Cookie管理")
print("=" * 60)
# 启动测试服务器
server = TestServer(port=8888)
server.start()
opts = FirefoxOptions()
opts.headless(False)
page = FirefoxPage(opts)
try:
# 访问测试服务器的Cookie设置页面
print("\n访问测试服务器...")
page.get(server.get_url("/set-cookie"))
page.wait(2)
# 1. 获取服务器设置的Cookie
print("\n1. 获取服务器设置的Cookie:")
all_cookies = page.get_cookies(all_info=True)
print(f" 共有 {len(all_cookies)} 个Cookie")
for cookie in all_cookies:
name = cookie.name
value = cookie.value
print(f" - {name}: {value}")
# 2. 通过API设置Cookie
print("\n2. 通过API设置Cookie:")
page.set_cookies(
{
"name": "api_cookie",
"value": "api_value",
"domain": "127.0.0.1",
"path": "/",
}
)
page.set_cookies(
{"name": "user_id", "value": "12345", "domain": "127.0.0.1", "path": "/"}
)
print(f" ✓ 已通过API设置2个Cookie")
# 3. 验证Cookie已设置
print("\n3. 验证Cookie已设置:")
page.refresh()
page.wait(1)
all_cookies = page.get_cookies(all_info=True)
print(f" 刷新后共有 {len(all_cookies)} 个Cookie")
# 转换为字典方便查找
cookies_dict = {}
for c in all_cookies:
cookies_dict[c.name] = c.value
if "api_cookie" in cookies_dict:
print(f" ✓ api_cookie = {cookies_dict['api_cookie']}")
if "user_id" in cookies_dict:
print(f" ✓ user_id = {cookies_dict['user_id']}")
# 4. 设置带过期时间的Cookie
print("\n4. 设置带过期时间的Cookie:")
expire_time = int(time.time()) + 3600 # 1小时后过期
page.set_cookies(
{
"name": "expire_cookie",
"value": "will_expire",
"domain": "127.0.0.1",
"path": "/",
"expiry": expire_time,
}
)
print(f" ✓ 已设置带过期时间的Cookie(1小时后过期)")
# 5. 设置HttpOnly和Secure Cookie
print("\n5. 设置HttpOnly和Secure Cookie:")
page.set_cookies(
{
"name": "secure_cookie",
"value": "secure_value",
"domain": "127.0.0.1",
"path": "/",
"httpOnly": True,
"secure": False, # HTTP不能用secure=True
}
)
print(f" ✓ 已设置HttpOnly Cookie")
# 6. 获取特定Cookie
print("\n6. 获取特定Cookie:")
all_cookies = page.get_cookies(all_info=True)
cookies_dict = {}
for c in all_cookies:
cookies_dict[c.name] = c.value
if "user_id" in cookies_dict:
print(f" user_id = {cookies_dict['user_id']}")
if "expire_cookie" in cookies_dict:
print(f" expire_cookie = {cookies_dict['expire_cookie']}")
# 6.1 简单 cookies 属性
print("\n6.1 当前页面 cookies 属性:")
simple_cookies = page.cookies
print(f" page.cookies 数量: {len(simple_cookies)}")
# 6.2 setter 风格 Cookie API
print("\n6.2 通过 page.set.cookies 设置Cookie:")
page.set.cookies(
{
"name": "setter_cookie",
"value": "setter_value",
"domain": "127.0.0.1",
"path": "/",
}
)
page.wait(0.5)
setter_cookies = {c.name: c.value for c in page.get_cookies()}
print(f" setter_cookie = {setter_cookies.get('setter_cookie')}")
# 6.3 通过高层过滤 API 读取 Cookie
print("\n6.3 通过高层过滤 API 读取Cookie:")
filtered_cookies = page.get_cookies_filtered(name="user_id", all_info=True)
print(f" 过滤结果数量: {len(filtered_cookies)}")
if filtered_cookies:
value = filtered_cookies[0].value
print(f" user_id(filtered) = {value}")
# 7. 删除特定Cookie
print("\n7. 删除特定Cookie:")
page.delete_cookies(name="api_cookie")
print(f" ✓ 已删除 api_cookie")
# 验证删除
page.wait(0.5)
all_cookies = page.get_cookies(all_info=True)
cookies_dict = {}
for c in all_cookies:
cookies_dict[c.name] = c.value
if "api_cookie" not in cookies_dict:
print(f" ✓ 确认 api_cookie 已被删除")
else:
print(f" ⚠ api_cookie 仍然存在")
# 7.1 通过公开删除接口删除 setter_cookie
print("\n7.1 删除 setter_cookie:")
page.delete_cookies(name="setter_cookie", domain="127.0.0.1")
page.wait(0.5)
cookies_after_remove = {c.name: c.value for c in page.get_cookies()}
print(f" setter_cookie 是否存在: {'setter_cookie' in cookies_after_remove}")
# 8. 访问API验证Cookie发送
print("\n8. 访问API验证Cookie发送:")
page.get(server.get_url("/get-cookie"))
page.wait(1)
body_text = page.ele("tag:body").text
print(f" 服务器收到的Cookie: {body_text[:100]}")
# 9. 清空所有Cookie
print("\n9. 清空所有Cookie:")
page.delete_cookies()
print(f" ✓ 已清空所有Cookie")
# 验证清空
all_cookies = page.get_cookies()
print(f" 清空后Cookie数量: {len(all_cookies)}")
# 9.1 浏览器级 Cookie 读取
print("\n9.1 浏览器级 cookies 读取:")
browser_cookies = page.browser.cookies(all_info=False)
print(f" browser.cookies 数量: {len(browser_cookies)}")
# 10. 重新设置并测试
print("\n10. 重新设置Cookie并测试:")
page.set_cookies(
{
"name": "final_cookie",
"value": "final_value",
"domain": "127.0.0.1",
"path": "/",
}
)
page.refresh()
page.wait(1)
all_cookies = page.get_cookies(all_info=True)
cookies_dict = {}
for c in all_cookies:
cookies_dict[c.name] = c.value
if "final_cookie" in cookies_dict:
print(f" ✓ final_cookie = {cookies_dict['final_cookie']}")
print(f" ✓ Cookie持久性测试通过")
print("\n" + "=" * 60)
print("✓ 所有Cookie管理测试通过!")
print("=" * 60)
except Exception as e:
print(f"\n✗ 测试失败: {e}")
import traceback
traceback.print_exc()
finally:
page.wait(2)
page.quit()
server.stop()
if __name__ == "__main__":
test_cookies()
展示 tab 作为日常页面操作的重要组织单位。
适合 tabs、browser 和 browsing-context 页面。
来源:app/content/ruyipage_examples/09_tabs.py · 仓库文件:09_tabs.py
# =============================
# RuyiPage 示例注释版
# 标题: 标签页管理:新建、切换、查找、关闭、后台激活
# 说明: 展示 tab 作为日常页面操作的重要组织单位。
# 你会学到:
# 1. 新建标签页并加载地址
# 2. 通过标题、索引等方式查找 tab
# 3. 获取所有标签页 ID
# 4. 激活后台标签页
# 5. 关闭单个标签或保留当前标签关闭其他标签
# 相关 API: page.new_tab, page.tabs_count, page.tab_ids, page.get_tab, page.get_tabs, page.latest_tab, page.close_other_tabs, page.browser.activate_tab
# =============================
# -*- coding: utf-8 -*-
"""
示例9: 标签页管理
测试功能:
- 新建标签页
- 切换标签页
- 关闭标签页
- 获取标签页列表
- 标签页操作
"""
import os
import sys
import io
# 设置控制台输出编码为UTF-8(Windows兼容)
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from ruyipage import FirefoxPage, FirefoxOptions
def test_tabs():
"""测试标签页管理功能"""
print("=" * 60)
print("测试9: 标签页管理")
print("=" * 60)
opts = FirefoxOptions()
opts.headless(False)
page = FirefoxPage(opts)
try:
# 加载测试页面
test_page = os.path.join(
os.path.dirname(__file__), "test_pages", "test_page.html"
)
test_url = "file:///" + os.path.abspath(test_page).replace("\\", "/")
page.get(test_url)
page.wait(1)
# 1. 获取当前标签页数量
print("\n1. 获取当前标签页数量:")
print(f" 标签页数量: {page.tabs_count}")
tab2_url = "data:text/html,<html><head><title>Example Tab</title></head><body>tab2</body></html>"
tab3_url = "data:text/html,<html><head><title>Wikipedia Tab</title></head><body>tab3</body></html>"
# 2. 新建标签页
print("\n2. 新建标签页:")
tab2 = page.new_tab(tab2_url)
page.wait(2)
print(f" ✓ 新标签页已创建")
print(f" 新标签页标题: {tab2.title}")
print(f" 当前标签页数量: {page.tabs_count}")
# 3. 再新建一个标签页
print("\n3. 再新建一个标签页:")
tab3 = page.new_tab(tab3_url)
page.wait(2)
print(f" ✓ 第三个标签页已创建")
print(f" 标签页标题: {tab3.title}")
print(f" 当前标签页数量: {page.tabs_count}")
# 4. 获取所有标签页ID
print("\n4. 获取所有标签页ID:")
tab_ids = page.tab_ids
print(f" 标签页ID列表: {tab_ids}")
print(" 当前页面 tab_id: {}".format(page.tab_id))
# 5. 通过序号获取标签页
print("\n5. 通过序号获取标签页:")
first_tab = page.get_tab(1)
print(f" 第1个标签页标题: {first_tab.title}")
second_tab = page.get_tab(2)
print(f" 第2个标签页标题: {second_tab.title}")
# 6. 切换到第一个标签页
print("\n6. 切换到第一个标签页:")
first_tab.get(test_url)
page.wait(1)
print(f" 当前标题: {first_tab.title}")
# 7. 获取最新的标签页
print("\n7. 获取最新的标签页:")
latest = page.latest_tab
print(f" 最新标签页标题: {latest.title}")
# 8. 通过标题查找标签页
print("\n8. 通过标题查找标签页:")
example_tab = page.get_tab(title="Example")
if example_tab:
print(f" 找到标签页: {example_tab.title}")
else:
print(f" 未找到包含'Example'的标签页")
# 9. 通过URL查找标签页
print("\n9. 通过URL查找标签页:")
wiki_tab = page.get_tab(title="Wikipedia")
if wiki_tab:
print(f" 找到标签页: {wiki_tab.title}")
else:
print(f" 未找到包含'wikipedia'的标签页")
# 10. 获取所有匹配的标签页
print("\n10. 获取所有标签页:")
all_tabs = page.get_tabs()
print(f" 共有 {len(all_tabs)} 个标签页")
for i, tab in enumerate(all_tabs, 1):
print(f" 标签页{i}: {tab.title[:50]}")
# 10.1 获取窗口句柄信息
print("\n10.1 获取客户端窗口信息:")
window_handles = page.browser.window_handles
print(f" clientWindows 数量: {len(window_handles)}")
if window_handles:
first_window = window_handles[0]
print(f" 首个窗口状态: {first_window.get('state')}")
# 10.2 读取 browsing context 树
print("\n10.2 获取 browsing context 树:")
tree = page.contexts.get_tree()
contexts = tree.contexts
print(f" 顶层 context 数量: {len(contexts)}")
if contexts:
print(f" 第一个 context: {contexts[0].context}")
# 11. 关闭一个标签页
print("\n11. 关闭第二个标签页:")
second_tab.close()
page.wait(1)
print(f" ✓ 标签页已关闭")
print(f" 剩余标签页数量: {page.tabs_count}")
# 12. 关闭其他标签页(保留第一个)
print("\n12. 关闭其他标签页:")
page.close_other_tabs(first_tab)
page.wait(1)
print(f" ✓ 其他标签页已关闭")
print(f" 剩余标签页数量: {page.tabs_count}")
# 13. 新建后台标签页
print("\n13. 新建后台标签页:")
bg_tab = page.new_tab(tab2_url, background=True)
page.wait(1)
print(f" ✓ 后台标签页已创建")
print(f" 当前标签页数量: {page.tabs_count}")
# 14. 激活标签页
print("\n14. 激活后台标签页:")
page.browser.activate_tab(bg_tab)
page.wait(1)
print(f" ✓ 标签页已激活")
# 15. 通过高层 context API 创建后台标签页
print("\n15. 通过高层context API创建后台标签页:")
bidi_tab_id = page.contexts.create_tab(
background=True,
reference_context=page.tab_id,
)
page.wait(1)
print(f" 高层新标签页ID: {bidi_tab_id}")
print(f" 当前标签页数量: {page.tabs_count}")
# 清理纯 BiDi 新建的标签页,避免影响退出顺序
bidi_tab = page.get_tab(bidi_tab_id)
if bidi_tab:
bidi_tab.close()
page.wait(1)
print(" ✓ 纯BiDi标签页已关闭")
print("\n" + "=" * 60)
print("✓ 所有标签页管理测试通过!")
print("=" * 60)
except Exception as e:
print(f"\n✗ 测试失败: {e}")
import traceback
traceback.print_exc()
finally:
page.wait(2)
page.quit()
if __name__ == "__main__":
test_tabs()
# -*- coding: utf-8 -*-
"""
示例9: 标签页管理
测试功能:
- 新建标签页
- 切换标签页
- 关闭标签页
- 获取标签页列表
- 标签页操作
"""
import os
import sys
import io
# 设置控制台输出编码为UTF-8(Windows兼容)
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from ruyipage import FirefoxPage, FirefoxOptions
def test_tabs():
"""测试标签页管理功能"""
print("=" * 60)
print("测试9: 标签页管理")
print("=" * 60)
opts = FirefoxOptions()
opts.headless(False)
page = FirefoxPage(opts)
try:
# 加载测试页面
test_page = os.path.join(
os.path.dirname(__file__), "test_pages", "test_page.html"
)
test_url = "file:///" + os.path.abspath(test_page).replace("\\", "/")
page.get(test_url)
page.wait(1)
# 1. 获取当前标签页数量
print("\n1. 获取当前标签页数量:")
print(f" 标签页数量: {page.tabs_count}")
tab2_url = "data:text/html,<html><head><title>Example Tab</title></head><body>tab2</body></html>"
tab3_url = "data:text/html,<html><head><title>Wikipedia Tab</title></head><body>tab3</body></html>"
# 2. 新建标签页
print("\n2. 新建标签页:")
tab2 = page.new_tab(tab2_url)
page.wait(2)
print(f" ✓ 新标签页已创建")
print(f" 新标签页标题: {tab2.title}")
print(f" 当前标签页数量: {page.tabs_count}")
# 3. 再新建一个标签页
print("\n3. 再新建一个标签页:")
tab3 = page.new_tab(tab3_url)
page.wait(2)
print(f" ✓ 第三个标签页已创建")
print(f" 标签页标题: {tab3.title}")
print(f" 当前标签页数量: {page.tabs_count}")
# 4. 获取所有标签页ID
print("\n4. 获取所有标签页ID:")
tab_ids = page.tab_ids
print(f" 标签页ID列表: {tab_ids}")
print(" 当前页面 tab_id: {}".format(page.tab_id))
# 5. 通过序号获取标签页
print("\n5. 通过序号获取标签页:")
first_tab = page.get_tab(1)
print(f" 第1个标签页标题: {first_tab.title}")
second_tab = page.get_tab(2)
print(f" 第2个标签页标题: {second_tab.title}")
# 6. 切换到第一个标签页
print("\n6. 切换到第一个标签页:")
first_tab.get(test_url)
page.wait(1)
print(f" 当前标题: {first_tab.title}")
# 7. 获取最新的标签页
print("\n7. 获取最新的标签页:")
latest = page.latest_tab
print(f" 最新标签页标题: {latest.title}")
# 8. 通过标题查找标签页
print("\n8. 通过标题查找标签页:")
example_tab = page.get_tab(title="Example")
if example_tab:
print(f" 找到标签页: {example_tab.title}")
else:
print(f" 未找到包含'Example'的标签页")
# 9. 通过URL查找标签页
print("\n9. 通过URL查找标签页:")
wiki_tab = page.get_tab(title="Wikipedia")
if wiki_tab:
print(f" 找到标签页: {wiki_tab.title}")
else:
print(f" 未找到包含'wikipedia'的标签页")
# 10. 获取所有匹配的标签页
print("\n10. 获取所有标签页:")
all_tabs = page.get_tabs()
print(f" 共有 {len(all_tabs)} 个标签页")
for i, tab in enumerate(all_tabs, 1):
print(f" 标签页{i}: {tab.title[:50]}")
# 10.1 获取窗口句柄信息
print("\n10.1 获取客户端窗口信息:")
window_handles = page.browser.window_handles
print(f" clientWindows 数量: {len(window_handles)}")
if window_handles:
first_window = window_handles[0]
print(f" 首个窗口状态: {first_window.get('state')}")
# 10.2 读取 browsing context 树
print("\n10.2 获取 browsing context 树:")
tree = page.contexts.get_tree()
contexts = tree.contexts
print(f" 顶层 context 数量: {len(contexts)}")
if contexts:
print(f" 第一个 context: {contexts[0].context}")
# 11. 关闭一个标签页
print("\n11. 关闭第二个标签页:")
second_tab.close()
page.wait(1)
print(f" ✓ 标签页已关闭")
print(f" 剩余标签页数量: {page.tabs_count}")
# 12. 关闭其他标签页(保留第一个)
print("\n12. 关闭其他标签页:")
page.close_other_tabs(first_tab)
page.wait(1)
print(f" ✓ 其他标签页已关闭")
print(f" 剩余标签页数量: {page.tabs_count}")
# 13. 新建后台标签页
print("\n13. 新建后台标签页:")
bg_tab = page.new_tab(tab2_url, background=True)
page.wait(1)
print(f" ✓ 后台标签页已创建")
print(f" 当前标签页数量: {page.tabs_count}")
# 14. 激活标签页
print("\n14. 激活后台标签页:")
page.browser.activate_tab(bg_tab)
page.wait(1)
print(f" ✓ 标签页已激活")
# 15. 通过高层 context API 创建后台标签页
print("\n15. 通过高层context API创建后台标签页:")
bidi_tab_id = page.contexts.create_tab(
background=True,
reference_context=page.tab_id,
)
page.wait(1)
print(f" 高层新标签页ID: {bidi_tab_id}")
print(f" 当前标签页数量: {page.tabs_count}")
# 清理纯 BiDi 新建的标签页,避免影响退出顺序
bidi_tab = page.get_tab(bidi_tab_id)
if bidi_tab:
bidi_tab.close()
page.wait(1)
print(" ✓ 纯BiDi标签页已关闭")
print("\n" + "=" * 60)
print("✓ 所有标签页管理测试通过!")
print("=" * 60)
except Exception as e:
print(f"\n✗ 测试失败: {e}")
import traceback
traceback.print_exc()
finally:
page.wait(2)
page.quit()
if __name__ == "__main__":
test_tabs()
系统覆盖 window 与 viewport 的浏览器级操作。
适合 window 与 browser 页面。
来源:app/content/ruyipage_examples/16_window_management.py · 仓库文件:16_window_management.py
# =============================
# RuyiPage 示例注释版
# 标题: 窗口管理:尺寸、位置、状态流与标签切换
# 说明: 系统覆盖 window 与 viewport 的浏览器级操作。
# 你会学到:
# 1. 读取窗口和视口尺寸
# 2. 调整窗口大小与位置
# 3. 切换最大化、最小化、全屏等状态
# 4. 多标签创建和激活切换
# 相关 API: page.rect.window_size, page.rect.viewport_size, page.rect.window_location, page.rect.page_size, page.window.maximize, page.window.minimize, page.window.fullscreen, page.window.set_size
# =============================
# -*- coding: utf-8 -*-
"""示例16: 浏览器窗口管理(完整场景)
测试覆盖:
1) 窗口/视口/页面尺寸与位置
2) 窗口状态流:normal -> maximize -> minimize -> fullscreen -> normal
3) 设置窗口大小、位置、居中
4) 多标签创建、激活、切换、关闭
"""
import os
import sys
import io
# 设置控制台输出编码为UTF-8(Windows兼容)
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from ruyipage import FirefoxPage, FirefoxOptions
def test_window_management():
"""测试浏览器窗口管理功能"""
print("=" * 60)
print("测试16: 浏览器窗口管理")
print("=" * 60)
opts = FirefoxOptions()
opts.headless(False)
page = FirefoxPage(opts)
try:
# 加载测试页面
test_page = os.path.join(
os.path.dirname(__file__), "test_pages", "test_page.html"
)
test_url = "file:///" + os.path.abspath(test_page).replace("\\", "/")
page.get(test_url)
page.wait(1)
# 1. 获取当前窗口大小
print("\n1. 获取当前窗口大小:")
size = page.rect.window_size
print(f" 窗口大小: {size[0]} x {size[1]}")
# 2. 获取视口大小
print("\n2. 获取视口大小:")
viewport = page.rect.viewport_size
print(f" 视口大小: {viewport[0]} x {viewport[1]}")
# 3. 获取窗口位置
print("\n3. 获取窗口位置:")
location = page.rect.window_location
print(f" 窗口位置: ({location[0]}, {location[1]})")
# 4. 获取页面完整大小
print("\n4. 获取页面完整大小:")
page_size = page.rect.page_size
print(f" 页面大小: {page_size[0]} x {page_size[1]}")
# 5. 获取滚动位置
print("\n5. 获取滚动位置:")
scroll_pos = page.rect.scroll_position
print(f" 滚动位置: ({scroll_pos[0]}, {scroll_pos[1]})")
# 6. 滚动页面并检查位置
print("\n6. 滚动页面并检查位置:")
page.scroll.to_bottom()
page.wait(0.5)
new_scroll = page.rect.scroll_position
print(f" 滚动后位置: ({new_scroll[0]}, {new_scroll[1]})")
# 6.1 窗口状态流
print("\n6.1 窗口状态流测试:")
page.window.maximize()
page.wait(0.5)
print(f" maximize 后窗口大小: {page.rect.window_size}")
page.window.minimize()
page.wait(0.5)
print(" ✓ minimize 已调用")
page.window.fullscreen()
page.wait(0.5)
print(f" fullscreen 后视口大小: {page.rect.viewport_size}")
page.window.normal()
page.wait(0.5)
print(" ✓ normal 已恢复")
# 6.2 设置窗口尺寸与位置并居中
print("\n6.2 设置窗口尺寸/位置/居中:")
page.window.set_size(1200, 820)
page.window.set_position(40, 40)
page.wait(0.5)
print(f" 调整后窗口大小: {page.rect.window_size}")
print(f" 调整后窗口位置: {page.rect.window_location}")
page.window.center()
page.wait(0.5)
print(f" 居中后窗口位置: {page.rect.window_location}")
# 7. 创建新标签页
print("\n7. 创建新标签页:")
new_tab = page.new_tab()
page.wait(1)
print(f" ✓ 新标签页已创建")
# 8. 在新标签页中打开页面
print("\n8. 在新标签页中打开页面:")
new_tab.get("https://www.example.com")
new_tab.wait(2)
print(f" 新标签页URL: {new_tab.url}")
print(f" 新标签页标题: {new_tab.title}")
# 9. 切换回原标签页
print("\n9. 切换回原标签页:")
# 使用 browser.activate_tab 显式切回原 tab
page.browser.activate_tab(page.tab_id)
page.wait(0.5)
page.get(test_url)
page.wait(0.5)
print(f" 当前标签页标题: {page.title}")
# 9.1 新建后台标签页并激活
print("\n9.1 后台标签页激活测试:")
bg_tab = page.new_tab("https://www.example.com", background=True)
page.wait(1)
print(f" 后台标签页ID: {bg_tab.tab_id}")
page.browser.activate_tab(bg_tab)
page.wait(1)
print(f" 激活后标题: {bg_tab.title}")
# 切回主 tab,避免后续关闭干扰
page.browser.activate_tab(page.tab_id)
page.wait(0.5)
# 10. 关闭新标签页
print("\n10. 关闭新标签页:")
new_tab.close()
try:
bg_tab.close()
except Exception:
pass
page.wait(0.5)
print(f" ✓ 新标签页已关闭")
print("\n" + "=" * 60)
print("✓ 所有窗口管理测试通过!")
print("=" * 60)
except Exception as e:
print(f"\n✗ 测试失败: {e}")
import traceback
traceback.print_exc()
finally:
page.wait(2)
page.quit()
if __name__ == "__main__":
test_window_management()
# -*- coding: utf-8 -*-
"""示例16: 浏览器窗口管理(完整场景)
测试覆盖:
1) 窗口/视口/页面尺寸与位置
2) 窗口状态流:normal -> maximize -> minimize -> fullscreen -> normal
3) 设置窗口大小、位置、居中
4) 多标签创建、激活、切换、关闭
"""
import os
import sys
import io
# 设置控制台输出编码为UTF-8(Windows兼容)
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from ruyipage import FirefoxPage, FirefoxOptions
def test_window_management():
"""测试浏览器窗口管理功能"""
print("=" * 60)
print("测试16: 浏览器窗口管理")
print("=" * 60)
opts = FirefoxOptions()
opts.headless(False)
page = FirefoxPage(opts)
try:
# 加载测试页面
test_page = os.path.join(
os.path.dirname(__file__), "test_pages", "test_page.html"
)
test_url = "file:///" + os.path.abspath(test_page).replace("\\", "/")
page.get(test_url)
page.wait(1)
# 1. 获取当前窗口大小
print("\n1. 获取当前窗口大小:")
size = page.rect.window_size
print(f" 窗口大小: {size[0]} x {size[1]}")
# 2. 获取视口大小
print("\n2. 获取视口大小:")
viewport = page.rect.viewport_size
print(f" 视口大小: {viewport[0]} x {viewport[1]}")
# 3. 获取窗口位置
print("\n3. 获取窗口位置:")
location = page.rect.window_location
print(f" 窗口位置: ({location[0]}, {location[1]})")
# 4. 获取页面完整大小
print("\n4. 获取页面完整大小:")
page_size = page.rect.page_size
print(f" 页面大小: {page_size[0]} x {page_size[1]}")
# 5. 获取滚动位置
print("\n5. 获取滚动位置:")
scroll_pos = page.rect.scroll_position
print(f" 滚动位置: ({scroll_pos[0]}, {scroll_pos[1]})")
# 6. 滚动页面并检查位置
print("\n6. 滚动页面并检查位置:")
page.scroll.to_bottom()
page.wait(0.5)
new_scroll = page.rect.scroll_position
print(f" 滚动后位置: ({new_scroll[0]}, {new_scroll[1]})")
# 6.1 窗口状态流
print("\n6.1 窗口状态流测试:")
page.window.maximize()
page.wait(0.5)
print(f" maximize 后窗口大小: {page.rect.window_size}")
page.window.minimize()
page.wait(0.5)
print(" ✓ minimize 已调用")
page.window.fullscreen()
page.wait(0.5)
print(f" fullscreen 后视口大小: {page.rect.viewport_size}")
page.window.normal()
page.wait(0.5)
print(" ✓ normal 已恢复")
# 6.2 设置窗口尺寸与位置并居中
print("\n6.2 设置窗口尺寸/位置/居中:")
page.window.set_size(1200, 820)
page.window.set_position(40, 40)
page.wait(0.5)
print(f" 调整后窗口大小: {page.rect.window_size}")
print(f" 调整后窗口位置: {page.rect.window_location}")
page.window.center()
page.wait(0.5)
print(f" 居中后窗口位置: {page.rect.window_location}")
# 7. 创建新标签页
print("\n7. 创建新标签页:")
new_tab = page.new_tab()
page.wait(1)
print(f" ✓ 新标签页已创建")
# 8. 在新标签页中打开页面
print("\n8. 在新标签页中打开页面:")
new_tab.get("https://www.example.com")
new_tab.wait(2)
print(f" 新标签页URL: {new_tab.url}")
print(f" 新标签页标题: {new_tab.title}")
# 9. 切换回原标签页
print("\n9. 切换回原标签页:")
# 使用 browser.activate_tab 显式切回原 tab
page.browser.activate_tab(page.tab_id)
page.wait(0.5)
page.get(test_url)
page.wait(0.5)
print(f" 当前标签页标题: {page.title}")
# 9.1 新建后台标签页并激活
print("\n9.1 后台标签页激活测试:")
bg_tab = page.new_tab("https://www.example.com", background=True)
page.wait(1)
print(f" 后台标签页ID: {bg_tab.tab_id}")
page.browser.activate_tab(bg_tab)
page.wait(1)
print(f" 激活后标题: {bg_tab.title}")
# 切回主 tab,避免后续关闭干扰
page.browser.activate_tab(page.tab_id)
page.wait(0.5)
# 10. 关闭新标签页
print("\n10. 关闭新标签页:")
new_tab.close()
try:
bg_tab.close()
except Exception:
pass
page.wait(0.5)
print(f" ✓ 新标签页已关闭")
print("\n" + "=" * 60)
print("✓ 所有窗口管理测试通过!")
print("=" * 60)
except Exception as e:
print(f"\n✗ 测试失败: {e}")
import traceback
traceback.print_exc()
finally:
page.wait(2)
page.quit()
if __name__ == "__main__":
test_window_management()
专门讲 alert、confirm、prompt 及其自动策略。
适合 prompts 页面。
来源:app/content/ruyipage_examples/17_user_prompts.py · 仓库文件:17_user_prompts.py
# =============================
# RuyiPage 示例注释版
# 标题: 用户提示框处理:自动策略与步骤式登录 API
# 说明: 专门讲 alert、confirm、prompt 及其自动策略。
# 你会学到:
# 1. 设置全局 alert / confirm / prompt 处理策略
# 2. 给 prompt 设置默认输入文本
# 3. 获取最近一次 prompt 打开和关闭信息
# 4. 使用步骤式 API 完成 prompt 登录
# 相关 API: opts.set_user_prompt_handler, page.set_prompt_handler, page.clear_prompt_handler, page.get_last_prompt_opened, page.get_last_prompt_closed, page.prompt_login
# =============================
# -*- coding: utf-8 -*-
"""示例17: 用户提示框处理(登录式步骤 API)
演示:
1) 自动策略 API
2) 步骤式 prompt 登录 API
3) 触发模式切换:mouse / keyboard
"""
import io
import os
import sys
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from ruyipage import FirefoxOptions, FirefoxPage
def test_user_prompts():
print("=" * 60)
print("测试17: 用户提示框处理")
print("=" * 60)
opts = FirefoxOptions()
opts.headless(False)
opts.set_user_prompt_handler(
{
"alert": "accept",
"confirm": "accept",
"prompt": "ignore",
"default": "accept",
}
)
page = FirefoxPage(opts)
try:
test_page = os.path.join(
os.path.dirname(__file__), "test_pages", "native_user_prompts_test.html"
)
test_url = "file:///" + os.path.abspath(test_page).replace("\\", "/")
page.get(test_url)
page.wait(0.8)
page.set_prompt_handler(
alert="accept",
confirm="accept",
prompt="ignore",
default="accept",
prompt_text="张三",
)
print("\n1. alert(自动策略):")
page.ele("#alert-btn").click_self()
page.wait(0.3)
print(f" result: {page.ele('#alert-result').text}")
print("\n2. confirm(自动策略):")
page.ele("#confirm-btn").click_self()
page.wait(0.3)
print(f" result: {page.ele('#confirm-result').text}")
print(f" opened: {page.get_last_prompt_opened()}")
print(f" closed: {page.get_last_prompt_closed()}")
print("\n3. prompt 登录(步骤式 API / mouse):")
page.clear_prompt_handler()
page.get(test_url)
page.wait(0.8)
page.prompt_login(
"#login-prompt-btn", "alice", "s3cr3t", trigger="mouse", timeout=2
)
page.wait(0.5)
print(f" result: {page.ele('#prompt-result').text}")
print("\n4. prompt 登录(步骤式 API / keyboard):")
page.get(test_url)
page.wait(0.8)
page.prompt_login(
"#login-prompt-btn", "bob", "654321", trigger="keyboard", timeout=2
)
page.wait(0.5)
print(f" result: {page.ele('#prompt-result').text}")
print("\n" + "=" * 60)
print("✓ 用户提示框自动策略 + 登录式步骤 API 测试完成!")
print("=" * 60)
except Exception as e:
print(f"\n✗ 测试失败: {e}")
import traceback
traceback.print_exc()
finally:
try:
page.clear_prompt_handler()
except Exception:
pass
page.wait(1)
page.quit()
if __name__ == "__main__":
test_user_prompts()
# -*- coding: utf-8 -*-
"""示例17: 用户提示框处理(登录式步骤 API)
演示:
1) 自动策略 API
2) 步骤式 prompt 登录 API
3) 触发模式切换:mouse / keyboard
"""
import io
import os
import sys
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from ruyipage import FirefoxOptions, FirefoxPage
def test_user_prompts():
print("=" * 60)
print("测试17: 用户提示框处理")
print("=" * 60)
opts = FirefoxOptions()
opts.headless(False)
opts.set_user_prompt_handler(
{
"alert": "accept",
"confirm": "accept",
"prompt": "ignore",
"default": "accept",
}
)
page = FirefoxPage(opts)
try:
test_page = os.path.join(
os.path.dirname(__file__), "test_pages", "native_user_prompts_test.html"
)
test_url = "file:///" + os.path.abspath(test_page).replace("\\", "/")
page.get(test_url)
page.wait(0.8)
page.set_prompt_handler(
alert="accept",
confirm="accept",
prompt="ignore",
default="accept",
prompt_text="张三",
)
print("\n1. alert(自动策略):")
page.ele("#alert-btn").click_self()
page.wait(0.3)
print(f" result: {page.ele('#alert-result').text}")
print("\n2. confirm(自动策略):")
page.ele("#confirm-btn").click_self()
page.wait(0.3)
print(f" result: {page.ele('#confirm-result').text}")
print(f" opened: {page.get_last_prompt_opened()}")
print(f" closed: {page.get_last_prompt_closed()}")
print("\n3. prompt 登录(步骤式 API / mouse):")
page.clear_prompt_handler()
page.get(test_url)
page.wait(0.8)
page.prompt_login(
"#login-prompt-btn", "alice", "s3cr3t", trigger="mouse", timeout=2
)
page.wait(0.5)
print(f" result: {page.ele('#prompt-result').text}")
print("\n4. prompt 登录(步骤式 API / keyboard):")
page.get(test_url)
page.wait(0.8)
page.prompt_login(
"#login-prompt-btn", "bob", "654321", trigger="keyboard", timeout=2
)
page.wait(0.5)
print(f" result: {page.ele('#prompt-result').text}")
print("\n" + "=" * 60)
print("✓ 用户提示框自动策略 + 登录式步骤 API 测试完成!")
print("=" * 60)
except Exception as e:
print(f"\n✗ 测试失败: {e}")
import traceback
traceback.print_exc()
finally:
try:
page.clear_prompt_handler()
except Exception:
pass
page.wait(1)
page.quit()
if __name__ == "__main__":
test_user_prompts()
集中说明 save_pdf 的参数覆盖能力。
适合 pdf 页面。
来源:app/content/ruyipage_examples/19_pdf_printing.py · 仓库文件:19_pdf_printing.py
# =============================
# RuyiPage 示例注释版
# 标题: PDF 打印:纸张尺寸、方向、边距、页范围与导出数据
# 说明: 集中说明 save_pdf 的参数覆盖能力。
# 你会学到:
# 1. 基础 PDF 导出
# 2. A4 纸张和背景打印
# 3. 横向打印与缩放
# 4. 自定义页边距和页范围
# 5. 获取 PDF bytes 与 base64
# 相关 API: page.save_pdf, page.pdf
# =============================
# -*- coding: utf-8 -*-
"""示例19: PDF 打印(完整参数覆盖)"""
import io
import os
import sys
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from ruyipage import FirefoxOptions, FirefoxPage
def _size_of(path):
return os.path.getsize(path) if os.path.exists(path) else 0
def test_pdf_printing():
print("=" * 60)
print("测试19: PDF打印功能")
print("=" * 60)
opts = FirefoxOptions()
opts.headless(False)
page = FirefoxPage(opts)
try:
test_page = os.path.join(
os.path.dirname(__file__), "test_pages", "test_page.html"
)
test_url = "file:///" + os.path.abspath(test_page).replace("\\", "/")
page.get(test_url)
page.wait(1)
output_dir = os.path.join(os.path.dirname(__file__), "output", "pdf")
os.makedirs(output_dir, exist_ok=True)
# 1) 基本打印
print("\n1. 基本PDF打印:")
p1 = os.path.join(output_dir, "basic.pdf")
page.save_pdf(p1)
print(f" ✓ 已保存: {p1} ({_size_of(p1)} bytes)")
# 2) A4 + 背景
print("\n2. A4 + 背景:")
p2 = os.path.join(output_dir, "a4_bg.pdf")
page.save_pdf(
p2,
page={"width": 21.0, "height": 29.7},
background=True,
)
print(f" ✓ 已保存: {p2} ({_size_of(p2)} bytes)")
# 3) 横向 + 缩放 + 页边距
print("\n3. 横向 + 缩放 + 页边距:")
p3 = os.path.join(output_dir, "landscape_scaled.pdf")
page.save_pdf(
p3,
orientation="landscape",
scale=0.9,
margin={
"top": 1.2,
"bottom": 1.2,
"left": 1.0,
"right": 1.0,
},
)
print(f" ✓ 已保存: {p3} ({_size_of(p3)} bytes)")
# 4) 指定页范围 + shrinkToFit
print("\n4. 指定页范围 + shrinkToFit:")
p4 = os.path.join(output_dir, "page_ranges_shrink.pdf")
page.save_pdf(
p4,
page_ranges=["1-2"],
shrink_to_fit=True,
)
print(f" ✓ 已保存: {p4} ({_size_of(p4)} bytes)")
# 5) 获取 bytes/base64
print("\n5. 获取 bytes / base64:")
pdf_bytes = page.pdf(page_ranges=["1"])
pdf_b64 = (
__import__("base64")
.b64encode(page.pdf(shrink_to_fit=False))
.decode("ascii")
)
print(f" bytes长度: {len(pdf_bytes)}")
print(f" base64长度: {len(pdf_b64)}")
if not (len(pdf_bytes) > 100 and len(pdf_b64) > 100):
raise RuntimeError("PDF 输出长度异常")
print("\n" + "=" * 60)
print("✓ 所有PDF打印测试通过!")
print(f"PDF保存在: {output_dir}")
print("=" * 60)
except Exception as e:
print(f"\n✗ 测试失败: {e}")
import traceback
traceback.print_exc()
finally:
page.wait(1)
page.quit()
if __name__ == "__main__":
test_pdf_printing()
# -*- coding: utf-8 -*-
"""示例19: PDF 打印(完整参数覆盖)"""
import io
import os
import sys
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from ruyipage import FirefoxOptions, FirefoxPage
def _size_of(path):
return os.path.getsize(path) if os.path.exists(path) else 0
def test_pdf_printing():
print("=" * 60)
print("测试19: PDF打印功能")
print("=" * 60)
opts = FirefoxOptions()
opts.headless(False)
page = FirefoxPage(opts)
try:
test_page = os.path.join(
os.path.dirname(__file__), "test_pages", "test_page.html"
)
test_url = "file:///" + os.path.abspath(test_page).replace("\\", "/")
page.get(test_url)
page.wait(1)
output_dir = os.path.join(os.path.dirname(__file__), "output", "pdf")
os.makedirs(output_dir, exist_ok=True)
# 1) 基本打印
print("\n1. 基本PDF打印:")
p1 = os.path.join(output_dir, "basic.pdf")
page.save_pdf(p1)
print(f" ✓ 已保存: {p1} ({_size_of(p1)} bytes)")
# 2) A4 + 背景
print("\n2. A4 + 背景:")
p2 = os.path.join(output_dir, "a4_bg.pdf")
page.save_pdf(
p2,
page={"width": 21.0, "height": 29.7},
background=True,
)
print(f" ✓ 已保存: {p2} ({_size_of(p2)} bytes)")
# 3) 横向 + 缩放 + 页边距
print("\n3. 横向 + 缩放 + 页边距:")
p3 = os.path.join(output_dir, "landscape_scaled.pdf")
page.save_pdf(
p3,
orientation="landscape",
scale=0.9,
margin={
"top": 1.2,
"bottom": 1.2,
"left": 1.0,
"right": 1.0,
},
)
print(f" ✓ 已保存: {p3} ({_size_of(p3)} bytes)")
# 4) 指定页范围 + shrinkToFit
print("\n4. 指定页范围 + shrinkToFit:")
p4 = os.path.join(output_dir, "page_ranges_shrink.pdf")
page.save_pdf(
p4,
page_ranges=["1-2"],
shrink_to_fit=True,
)
print(f" ✓ 已保存: {p4} ({_size_of(p4)} bytes)")
# 5) 获取 bytes/base64
print("\n5. 获取 bytes / base64:")
pdf_bytes = page.pdf(page_ranges=["1"])
pdf_b64 = (
__import__("base64")
.b64encode(page.pdf(shrink_to_fit=False))
.decode("ascii")
)
print(f" bytes长度: {len(pdf_bytes)}")
print(f" base64长度: {len(pdf_b64)}")
if not (len(pdf_bytes) > 100 and len(pdf_b64) > 100):
raise RuntimeError("PDF 输出长度异常")
print("\n" + "=" * 60)
print("✓ 所有PDF打印测试通过!")
print(f"PDF保存在: {output_dir}")
print("=" * 60)
except Exception as e:
print(f"\n✗ 测试失败: {e}")
import traceback
traceback.print_exc()
finally:
page.wait(1)
page.quit()
if __name__ == "__main__":
test_pdf_printing()
覆盖扩展安装、验证注入和卸载,是浏览器级能力的重要专题。
适合 extensions 页面。
来源:app/content/ruyipage_examples/22_web_extension.py · 仓库文件:22_web_extension.py
#!/usr/bin/env python
# =============================
# RuyiPage 示例注释版
# 标题: WebExtension:目录扩展与 XPI 安装卸载
# 说明: 覆盖扩展安装、验证注入和卸载,是浏览器级能力的重要专题。
# 你会学到:
# 1. 安装目录扩展
# 2. 验证 content script 生效
# 3. 打包并安装 XPI 扩展
# 4. 卸载指定扩展与清空测试扩展
# 相关 API: page.extensions.install, page.extensions.install_archive, page.extensions.uninstall, page.extensions.uninstall_all, page.run_js
# =============================
# -*- coding: utf-8 -*-
"""示例22: WebExtension 模块测试(完整链路版)
完整链路:
1) 创建测试扩展
2) 安装目录扩展
3) 验证扩展已安装
4) 卸载目录扩展
5) 打包 xpi 并安装
6) 验证扩展已安装
7) 卸载打包扩展
8) 清理测试文件
说明:
- WebExtension BiDi 规范只提供 install / uninstall 命令。
- 当前 Firefox 环境下若返回 unknown command,则视为“当前版本不支持”,不是脚本错误。
"""
import io
import json
import os
import shutil
import sys
import zipfile
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
from ruyipage import FirefoxPage
class Rows:
def __init__(self):
self.rows = []
def add(self, name, status, detail=""):
self.rows.append((name, status, detail))
print(f" {status}: {name}")
if detail:
print(f" 详情: {detail}")
def summary(self):
print("\n" + "=" * 70)
print("测试结果汇总")
print("=" * 70)
print(f"{'序号':<5} {'项目':<28} {'状态':<8} {'说明'}")
print("-" * 70)
for i, (name, status, detail) in enumerate(self.rows, 1):
print(f"{i:<5} {name:<28} {status:<8} {detail[:36]}")
print("-" * 70)
def _create_test_extension(ext_dir):
os.makedirs(ext_dir, exist_ok=True)
manifest = {
"manifest_version": 2,
"name": "RuyiPage Test Extension",
"version": "1.0.0",
"description": "测试扩展",
"browser_specific_settings": {
"gecko": {
"id": "[email protected]",
"strict_min_version": "109.0",
}
},
"background": {"scripts": ["background.js"]},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_end",
}
],
"permissions": [],
}
with open(os.path.join(ext_dir, "manifest.json"), "w", encoding="utf-8") as f:
json.dump(manifest, f, indent=2, ensure_ascii=False)
with open(os.path.join(ext_dir, "background.js"), "w", encoding="utf-8") as f:
f.write("console.log('RuyiPage Test Extension loaded!');")
with open(os.path.join(ext_dir, "content.js"), "w", encoding="utf-8") as f:
f.write(
"document.documentElement.setAttribute('data-ruyi-extension', 'loaded');"
)
def _page_has_extension_marker(page):
return (
page.run_js(
"return document.documentElement.getAttribute('data-ruyi-extension')"
)
== "loaded"
)
def _safe_install(page, path, rows, label):
try:
ext_id = page.extensions.install(path)
if ext_id:
rows.add(label, "✓ 通过", f"extension_id={ext_id}")
return ext_id
rows.add(label, "⚠ 不支持", "当前 Firefox 未返回 extension id")
return None
except Exception as e:
msg = str(e).lower()
if "unknown command" in msg or "not supported" in msg:
rows.add(label, "⚠ 不支持", "当前 Firefox 不支持 webExtension.install")
return None
raise
def test_web_extension():
print("=" * 70)
print("测试 22: WebExtension 模块")
print("=" * 70)
page = FirefoxPage()
rows = Rows()
ext_dir = os.path.join(os.path.dirname(__file__), "test_extension")
xpi_path = os.path.join(os.path.dirname(__file__), "test_extension.xpi")
ext_id = None
ext_id2 = None
try:
# 1) 创建测试扩展
_create_test_extension(ext_dir)
rows.add("创建测试扩展", "✓ 通过", ext_dir)
# 2) 安装目录扩展
ext_id = _safe_install(page, ext_dir, rows, "安装目录扩展")
# 3) 验证目录扩展已生效
if ext_id:
page.get("https://example.com")
page.wait(1.5)
marker = _page_has_extension_marker(page)
rows.add(
"目录扩展生效验证",
"✓ 通过" if marker else "✗ 失败",
"content script 已注入" if marker else "未检测到注入标记",
)
else:
rows.add("目录扩展生效验证", "⚠ 跳过", "安装未成功")
# 4) 卸载目录扩展
if ext_id:
page.extensions.uninstall(ext_id)
rows.add("卸载目录扩展", "✓ 通过")
else:
rows.add("卸载目录扩展", "⚠ 跳过", "未安装成功")
# 5) 打包 xpi
with zipfile.ZipFile(xpi_path, "w", zipfile.ZIP_DEFLATED) as zipf:
zipf.write(os.path.join(ext_dir, "manifest.json"), "manifest.json")
zipf.write(os.path.join(ext_dir, "background.js"), "background.js")
zipf.write(os.path.join(ext_dir, "content.js"), "content.js")
rows.add("打包 XPI", "✓ 通过", xpi_path)
# 6) 安装打包扩展
try:
ext_id2 = page.extensions.install_archive(xpi_path)
if ext_id2:
rows.add("安装 XPI 扩展", "✓ 通过", f"extension_id={ext_id2}")
else:
rows.add(
"安装 XPI 扩展", "⚠ 不支持", "当前 Firefox 未返回 extension id"
)
except Exception as e:
msg = str(e).lower()
if "unknown command" in msg or "not supported" in msg:
rows.add(
"安装 XPI 扩展",
"⚠ 不支持",
"当前 Firefox 不支持 webExtension.install",
)
else:
raise
# 7) 验证 XPI 扩展已生效
if ext_id2:
page.get("https://example.com")
page.wait(1.5)
marker = _page_has_extension_marker(page)
rows.add(
"XPI 扩展生效验证",
"✓ 通过" if marker else "✗ 失败",
"content script 已注入" if marker else "未检测到注入标记",
)
else:
rows.add("XPI 扩展生效验证", "⚠ 跳过", "安装未成功")
# 8) 卸载打包扩展
if ext_id2:
page.extensions.uninstall(ext_id2)
rows.add("卸载 XPI 扩展", "✓ 通过")
else:
rows.add("卸载 XPI 扩展", "⚠ 跳过", "未安装成功")
print("\n" + "=" * 70)
print("✓ WebExtension 模块测试完成")
print("=" * 70)
except Exception as e:
rows.add("WebExtension 模块", "✗ 失败", str(e))
import traceback
traceback.print_exc()
finally:
try:
page.extensions.uninstall_all()
except Exception:
pass
page.quit()
if os.path.exists(ext_dir):
shutil.rmtree(ext_dir)
if os.path.exists(xpi_path):
os.remove(xpi_path)
rows.add("清理测试文件", "✓ 通过")
rows.summary()
if __name__ == "__main__":
test_web_extension()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""示例22: WebExtension 模块测试(完整链路版)
完整链路:
1) 创建测试扩展
2) 安装目录扩展
3) 验证扩展已安装
4) 卸载目录扩展
5) 打包 xpi 并安装
6) 验证扩展已安装
7) 卸载打包扩展
8) 清理测试文件
说明:
- WebExtension BiDi 规范只提供 install / uninstall 命令。
- 当前 Firefox 环境下若返回 unknown command,则视为“当前版本不支持”,不是脚本错误。
"""
import io
import json
import os
import shutil
import sys
import zipfile
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
from ruyipage import FirefoxPage
class Rows:
def __init__(self):
self.rows = []
def add(self, name, status, detail=""):
self.rows.append((name, status, detail))
print(f" {status}: {name}")
if detail:
print(f" 详情: {detail}")
def summary(self):
print("\n" + "=" * 70)
print("测试结果汇总")
print("=" * 70)
print(f"{'序号':<5} {'项目':<28} {'状态':<8} {'说明'}")
print("-" * 70)
for i, (name, status, detail) in enumerate(self.rows, 1):
print(f"{i:<5} {name:<28} {status:<8} {detail[:36]}")
print("-" * 70)
def _create_test_extension(ext_dir):
os.makedirs(ext_dir, exist_ok=True)
manifest = {
"manifest_version": 2,
"name": "RuyiPage Test Extension",
"version": "1.0.0",
"description": "测试扩展",
"browser_specific_settings": {
"gecko": {
"id": "[email protected]",
"strict_min_version": "109.0",
}
},
"background": {"scripts": ["background.js"]},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_end",
}
],
"permissions": [],
}
with open(os.path.join(ext_dir, "manifest.json"), "w", encoding="utf-8") as f:
json.dump(manifest, f, indent=2, ensure_ascii=False)
with open(os.path.join(ext_dir, "background.js"), "w", encoding="utf-8") as f:
f.write("console.log('RuyiPage Test Extension loaded!');")
with open(os.path.join(ext_dir, "content.js"), "w", encoding="utf-8") as f:
f.write(
"document.documentElement.setAttribute('data-ruyi-extension', 'loaded');"
)
def _page_has_extension_marker(page):
return (
page.run_js(
"return document.documentElement.getAttribute('data-ruyi-extension')"
)
== "loaded"
)
def _safe_install(page, path, rows, label):
try:
ext_id = page.extensions.install(path)
if ext_id:
rows.add(label, "✓ 通过", f"extension_id={ext_id}")
return ext_id
rows.add(label, "⚠ 不支持", "当前 Firefox 未返回 extension id")
return None
except Exception as e:
msg = str(e).lower()
if "unknown command" in msg or "not supported" in msg:
rows.add(label, "⚠ 不支持", "当前 Firefox 不支持 webExtension.install")
return None
raise
def test_web_extension():
print("=" * 70)
print("测试 22: WebExtension 模块")
print("=" * 70)
page = FirefoxPage()
rows = Rows()
ext_dir = os.path.join(os.path.dirname(__file__), "test_extension")
xpi_path = os.path.join(os.path.dirname(__file__), "test_extension.xpi")
ext_id = None
ext_id2 = None
try:
# 1) 创建测试扩展
_create_test_extension(ext_dir)
rows.add("创建测试扩展", "✓ 通过", ext_dir)
# 2) 安装目录扩展
ext_id = _safe_install(page, ext_dir, rows, "安装目录扩展")
# 3) 验证目录扩展已生效
if ext_id:
page.get("https://example.com")
page.wait(1.5)
marker = _page_has_extension_marker(page)
rows.add(
"目录扩展生效验证",
"✓ 通过" if marker else "✗ 失败",
"content script 已注入" if marker else "未检测到注入标记",
)
else:
rows.add("目录扩展生效验证", "⚠ 跳过", "安装未成功")
# 4) 卸载目录扩展
if ext_id:
page.extensions.uninstall(ext_id)
rows.add("卸载目录扩展", "✓ 通过")
else:
rows.add("卸载目录扩展", "⚠ 跳过", "未安装成功")
# 5) 打包 xpi
with zipfile.ZipFile(xpi_path, "w", zipfile.ZIP_DEFLATED) as zipf:
zipf.write(os.path.join(ext_dir, "manifest.json"), "manifest.json")
zipf.write(os.path.join(ext_dir, "background.js"), "background.js")
zipf.write(os.path.join(ext_dir, "content.js"), "content.js")
rows.add("打包 XPI", "✓ 通过", xpi_path)
# 6) 安装打包扩展
try:
ext_id2 = page.extensions.install_archive(xpi_path)
if ext_id2:
rows.add("安装 XPI 扩展", "✓ 通过", f"extension_id={ext_id2}")
else:
rows.add(
"安装 XPI 扩展", "⚠ 不支持", "当前 Firefox 未返回 extension id"
)
except Exception as e:
msg = str(e).lower()
if "unknown command" in msg or "not supported" in msg:
rows.add(
"安装 XPI 扩展",
"⚠ 不支持",
"当前 Firefox 不支持 webExtension.install",
)
else:
raise
# 7) 验证 XPI 扩展已生效
if ext_id2:
page.get("https://example.com")
page.wait(1.5)
marker = _page_has_extension_marker(page)
rows.add(
"XPI 扩展生效验证",
"✓ 通过" if marker else "✗ 失败",
"content script 已注入" if marker else "未检测到注入标记",
)
else:
rows.add("XPI 扩展生效验证", "⚠ 跳过", "安装未成功")
# 8) 卸载打包扩展
if ext_id2:
page.extensions.uninstall(ext_id2)
rows.add("卸载 XPI 扩展", "✓ 通过")
else:
rows.add("卸载 XPI 扩展", "⚠ 跳过", "未安装成功")
print("\n" + "=" * 70)
print("✓ WebExtension 模块测试完成")
print("=" * 70)
except Exception as e:
rows.add("WebExtension 模块", "✗ 失败", str(e))
import traceback
traceback.print_exc()
finally:
try:
page.extensions.uninstall_all()
except Exception:
pass
page.quit()
if os.path.exists(ext_dir):
shutil.rmtree(ext_dir)
if os.path.exists(xpi_path):
os.remove(xpi_path)
rows.add("清理测试文件", "✓ 通过")
rows.summary()
if __name__ == "__main__":
test_web_extension()
讲清下载事件链和文件落盘验证,是 downloads 页的主示例。
适合 downloads 与 events 页面。
来源:app/content/ruyipage_examples/23_download.py · 仓库文件:23_download.py
#!/usr/bin/env python
# =============================
# RuyiPage 示例注释版
# 标题: 下载管理:下载目录、事件订阅、等待落盘、禁止下载
# 说明: 讲清下载事件链和文件落盘验证,是 downloads 页的主示例。
# 你会学到:
# 1. 设置 allow 下载行为并指定目录
# 2. 订阅下载开始和结束事件
# 3. 等待文本和 JSON 文件下载完成
# 4. 验证文件已落盘
# 5. 切换 deny 模式阻止下载
# 相关 API: page.downloads.set_behavior, page.downloads.start, page.downloads.wait_chain, page.downloads.wait, page.downloads.wait_file, page.downloads.stop
# =============================
# -*- coding: utf-8 -*-
"""示例23: 下载管理测试(严格结果版)"""
import io
import os
import shutil
import sys
from typing import List, Dict
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
from ruyipage import FirefoxPage
from ruyipage._functions.tools import find_free_port
from test_server import TestServer
def add_result(
results: List[Dict[str, str]], item: str, status: str, note: str
) -> None:
"""记录结构化测试结果。"""
results.append({"item": item, "status": status, "note": note})
def print_results(results: List[Dict[str, str]]) -> None:
"""打印固定格式结果表。"""
print("\n| 项目 | 状态 | 说明 |")
print("| --- | --- | --- |")
for row in results:
print(f"| {row['item']} | {row['status']} | {row['note']} |")
def build_download_page(base_url: str) -> str:
"""构造下载测试页。"""
return """
<!DOCTYPE html>
<html>
<head><meta charset='utf-8'><title>下载测试</title></head>
<body>
<button id='download-text'>下载文本文件</button>
<button id='download-json'>下载JSON文件</button>
<div id='status'></div>
<script>
function triggerDownload(url, statusText) {
const a = document.createElement('a');
a.href = url;
a.click();
document.getElementById('status').textContent = statusText;
}
document.getElementById('download-text').onclick = () => {
triggerDownload('%s/download/text', '文本文件下载已触发');
};
document.getElementById('download-json').onclick = () => {
triggerDownload('%s/download/json', 'JSON文件下载已触发');
};
</script>
</body>
</html>
""" % (base_url, base_url)
def main() -> None:
print("=" * 70)
print("测试 23: Download 下载管理测试")
print("=" * 70)
page = FirefoxPage()
server = None
results: List[Dict[str, str]] = []
download_path = os.path.abspath(
os.path.join(os.path.dirname(__file__), "downloads")
)
text_path = os.path.join(download_path, "test.txt")
json_path = os.path.join(download_path, "test.json")
try:
if os.path.exists(download_path):
shutil.rmtree(download_path)
os.makedirs(download_path, exist_ok=True)
server = TestServer(port=find_free_port(8930, 9030)).start()
base_url = server.get_url("")[:-1]
# 1. 允许下载
try:
page.downloads.set_behavior("allow", path=download_path)
add_result(
results,
"browser.setDownloadBehavior allow",
"成功",
f"下载目录: {download_path}",
)
except Exception as e:
add_result(
results, "browser.setDownloadBehavior allow", "失败", str(e)[:120]
)
raise
# 2. 加载页面
page.get("data:text/html;charset=utf-8," + build_download_page(base_url))
add_result(
results, "下载测试页加载", "成功", f"下载页已加载,服务地址: {base_url}"
)
# 3. 订阅下载事件
subscribed = page.downloads.start()
if subscribed:
add_result(
results,
"下载事件订阅",
"成功",
"已订阅 downloadWillBegin / downloadEnd",
)
else:
add_result(
results, "下载事件订阅", "不支持", "当前浏览器未成功订阅下载事件"
)
# 4. 文本文件下载
page.downloads.clear()
page.ele("#download-text").click_self()
begin_text, end_text = page.downloads.wait_chain(filename="test.txt", timeout=5)
if begin_text:
add_result(
results,
"downloadWillBegin text",
"成功",
f"filename={begin_text.suggested_filename or 'unknown'}",
)
else:
add_result(
results, "downloadWillBegin text", "失败", "5 秒内未收到开始事件"
)
if end_text:
end_status = end_text.status or "unknown"
add_result(results, "downloadEnd text", "成功", f"status={end_status}")
else:
add_result(results, "downloadEnd text", "失败", "5 秒内未收到结束事件")
if page.downloads.wait_file(text_path, timeout=3):
add_result(
results, "text 文件落盘", "成功", f"{os.path.getsize(text_path)} bytes"
)
else:
add_result(results, "text 文件落盘", "失败", "事件触发后仍未观察到文件落盘")
# 5. JSON 文件下载
page.downloads.clear()
page.ele("#download-json").click_self()
begin_json, end_json = page.downloads.wait_chain(
filename="test.json", timeout=5
)
if begin_json:
add_result(
results,
"downloadWillBegin json",
"成功",
f"filename={begin_json.suggested_filename or 'unknown'}",
)
else:
add_result(
results, "downloadWillBegin json", "失败", "5 秒内未收到开始事件"
)
if end_json:
end_status = end_json.status or "unknown"
add_result(results, "downloadEnd json", "成功", f"status={end_status}")
else:
add_result(results, "downloadEnd json", "失败", "5 秒内未收到结束事件")
if page.downloads.wait_file(json_path, timeout=3):
add_result(
results, "json 文件落盘", "成功", f"{os.path.getsize(json_path)} bytes"
)
else:
add_result(results, "json 文件落盘", "失败", "事件触发后仍未观察到文件落盘")
# 6. deny 模式
page.downloads.clear()
try:
page.downloads.set_behavior("deny")
page.ele("#download-text").click_self()
denied_begin = page.downloads.wait(
method="browsingContext.downloadWillBegin",
timeout=2,
filename="test.txt",
)
if denied_begin:
add_result(
results,
"browser.setDownloadBehavior deny",
"失败",
"deny 模式下仍出现下载开始事件",
)
else:
add_result(
results,
"browser.setDownloadBehavior deny",
"成功",
"deny 模式下未观察到下载开始事件",
)
except Exception as e:
add_result(
results, "browser.setDownloadBehavior deny", "失败", str(e)[:120]
)
print_results(results)
except Exception as e:
add_result(results, "示例执行", "失败", str(e)[:120])
print_results(results)
raise
finally:
try:
page.downloads.stop()
except Exception:
pass
try:
if server is not None:
server.stop()
except Exception:
pass
try:
page.quit()
except Exception:
pass
if os.path.exists(download_path):
shutil.rmtree(download_path)
if __name__ == "__main__":
main()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""示例23: 下载管理测试(严格结果版)"""
import io
import os
import shutil
import sys
from typing import List, Dict
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
from ruyipage import FirefoxPage
from ruyipage._functions.tools import find_free_port
from test_server import TestServer
def add_result(
results: List[Dict[str, str]], item: str, status: str, note: str
) -> None:
"""记录结构化测试结果。"""
results.append({"item": item, "status": status, "note": note})
def print_results(results: List[Dict[str, str]]) -> None:
"""打印固定格式结果表。"""
print("\n| 项目 | 状态 | 说明 |")
print("| --- | --- | --- |")
for row in results:
print(f"| {row['item']} | {row['status']} | {row['note']} |")
def build_download_page(base_url: str) -> str:
"""构造下载测试页。"""
return """
<!DOCTYPE html>
<html>
<head><meta charset='utf-8'><title>下载测试</title></head>
<body>
<button id='download-text'>下载文本文件</button>
<button id='download-json'>下载JSON文件</button>
<div id='status'></div>
<script>
function triggerDownload(url, statusText) {
const a = document.createElement('a');
a.href = url;
a.click();
document.getElementById('status').textContent = statusText;
}
document.getElementById('download-text').onclick = () => {
triggerDownload('%s/download/text', '文本文件下载已触发');
};
document.getElementById('download-json').onclick = () => {
triggerDownload('%s/download/json', 'JSON文件下载已触发');
};
</script>
</body>
</html>
""" % (base_url, base_url)
def main() -> None:
print("=" * 70)
print("测试 23: Download 下载管理测试")
print("=" * 70)
page = FirefoxPage()
server = None
results: List[Dict[str, str]] = []
download_path = os.path.abspath(
os.path.join(os.path.dirname(__file__), "downloads")
)
text_path = os.path.join(download_path, "test.txt")
json_path = os.path.join(download_path, "test.json")
try:
if os.path.exists(download_path):
shutil.rmtree(download_path)
os.makedirs(download_path, exist_ok=True)
server = TestServer(port=find_free_port(8930, 9030)).start()
base_url = server.get_url("")[:-1]
# 1. 允许下载
try:
page.downloads.set_behavior("allow", path=download_path)
add_result(
results,
"browser.setDownloadBehavior allow",
"成功",
f"下载目录: {download_path}",
)
except Exception as e:
add_result(
results, "browser.setDownloadBehavior allow", "失败", str(e)[:120]
)
raise
# 2. 加载页面
page.get("data:text/html;charset=utf-8," + build_download_page(base_url))
add_result(
results, "下载测试页加载", "成功", f"下载页已加载,服务地址: {base_url}"
)
# 3. 订阅下载事件
subscribed = page.downloads.start()
if subscribed:
add_result(
results,
"下载事件订阅",
"成功",
"已订阅 downloadWillBegin / downloadEnd",
)
else:
add_result(
results, "下载事件订阅", "不支持", "当前浏览器未成功订阅下载事件"
)
# 4. 文本文件下载
page.downloads.clear()
page.ele("#download-text").click_self()
begin_text, end_text = page.downloads.wait_chain(filename="test.txt", timeout=5)
if begin_text:
add_result(
results,
"downloadWillBegin text",
"成功",
f"filename={begin_text.suggested_filename or 'unknown'}",
)
else:
add_result(
results, "downloadWillBegin text", "失败", "5 秒内未收到开始事件"
)
if end_text:
end_status = end_text.status or "unknown"
add_result(results, "downloadEnd text", "成功", f"status={end_status}")
else:
add_result(results, "downloadEnd text", "失败", "5 秒内未收到结束事件")
if page.downloads.wait_file(text_path, timeout=3):
add_result(
results, "text 文件落盘", "成功", f"{os.path.getsize(text_path)} bytes"
)
else:
add_result(results, "text 文件落盘", "失败", "事件触发后仍未观察到文件落盘")
# 5. JSON 文件下载
page.downloads.clear()
page.ele("#download-json").click_self()
begin_json, end_json = page.downloads.wait_chain(
filename="test.json", timeout=5
)
if begin_json:
add_result(
results,
"downloadWillBegin json",
"成功",
f"filename={begin_json.suggested_filename or 'unknown'}",
)
else:
add_result(
results, "downloadWillBegin json", "失败", "5 秒内未收到开始事件"
)
if end_json:
end_status = end_json.status or "unknown"
add_result(results, "downloadEnd json", "成功", f"status={end_status}")
else:
add_result(results, "downloadEnd json", "失败", "5 秒内未收到结束事件")
if page.downloads.wait_file(json_path, timeout=3):
add_result(
results, "json 文件落盘", "成功", f"{os.path.getsize(json_path)} bytes"
)
else:
add_result(results, "json 文件落盘", "失败", "事件触发后仍未观察到文件落盘")
# 6. deny 模式
page.downloads.clear()
try:
page.downloads.set_behavior("deny")
page.ele("#download-text").click_self()
denied_begin = page.downloads.wait(
method="browsingContext.downloadWillBegin",
timeout=2,
filename="test.txt",
)
if denied_begin:
add_result(
results,
"browser.setDownloadBehavior deny",
"失败",
"deny 模式下仍出现下载开始事件",
)
else:
add_result(
results,
"browser.setDownloadBehavior deny",
"成功",
"deny 模式下未观察到下载开始事件",
)
except Exception as e:
add_result(
results, "browser.setDownloadBehavior deny", "失败", str(e)[:120]
)
print_results(results)
except Exception as e:
add_result(results, "示例执行", "失败", str(e)[:120])
print_results(results)
raise
finally:
try:
page.downloads.stop()
except Exception:
pass
try:
if server is not None:
server.stop()
except Exception:
pass
try:
page.quit()
except Exception:
pass
if os.path.exists(download_path):
shutil.rmtree(download_path)
if __name__ == "__main__":
main()
Browser 模块最完整的 user context 与 client window 说明案例。
适合 browser、user-contexts 和 window 页面。
来源:app/content/ruyipage_examples/25_browser_user_context.py · 仓库文件:25_browser_user_context.py
#!/usr/bin/env python
# =============================
# RuyiPage 示例注释版
# 标题: Browser 模块:用户上下文与客户端窗口
# 说明: Browser 模块最完整的 user context 与 client window 说明案例。
# 你会学到:
# 1. 读取 user context 列表
# 2. 创建与删除 user context
# 3. 在指定 user context 中创建新 tab
# 4. 读取 client window 列表
# 5. 切换窗口状态为 minimized / maximized / fullscreen
# 相关 API: page.browser_tools.get_user_contexts, page.browser_tools.create_user_context, page.browser_tools.remove_user_context, page.browser_tools.create_tab, page.browser_tools.get_client_windows, page.browser_tools.set_window_state
# =============================
# -*- coding: utf-8 -*-
"""示例25: Browser 模块(用户上下文 + 客户端窗口)严格结果版"""
import io
import sys
import time
from typing import Dict, List
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
from ruyipage import FirefoxPage
def add_result(
results: List[Dict[str, str]], item: str, status: str, note: str
) -> None:
"""记录结构化结果。"""
results.append({"item": item, "status": status, "note": note})
def print_results(results: List[Dict[str, str]]) -> None:
"""打印固定格式结果表。"""
print("\n| 项目 | 状态 | 说明 |")
print("| --- | --- | --- |")
for row in results:
print(f"| {row['item']} | {row['status']} | {row['note']} |")
def main() -> None:
print("=" * 70)
print("测试 25: Browser 用户上下文与窗口管理")
print("=" * 70)
page = FirefoxPage()
results: List[Dict[str, str]] = []
user_context_id = None
new_tab_ctx = None
try:
page.get("https://www.example.com")
# 1. user context 生命周期
before = page.browser_tools.get_user_contexts()
before_ids = [i.get("userContext") for i in before]
add_result(
results,
"browser.getUserContexts before",
"成功",
f"创建前数量: {len(before)}",
)
user_context_id = page.browser_tools.create_user_context()
if user_context_id:
add_result(
results,
"browser.createUserContext",
"成功",
f"userContext={user_context_id}",
)
else:
add_result(
results, "browser.createUserContext", "失败", "未返回 userContext ID"
)
after_create = page.browser_tools.get_user_contexts()
after_create_ids = [i.get("userContext") for i in after_create]
if user_context_id in after_create_ids and len(after_create) == len(before) + 1:
add_result(
results,
"browser.getUserContexts after create",
"成功",
f"数量变为 {len(after_create)}",
)
else:
add_result(
results,
"browser.getUserContexts after create",
"失败",
"创建后数量或ID校验不通过",
)
if user_context_id:
new_tab_ctx = page.browser_tools.create_tab(user_context=user_context_id)
if new_tab_ctx:
add_result(
results,
"browsingContext.create tab in user context",
"成功",
f"context={new_tab_ctx}",
)
else:
add_result(
results,
"browsingContext.create tab in user context",
"失败",
"未返回新 tab context",
)
if new_tab_ctx:
try:
page.contexts.close(new_tab_ctx)
add_result(
results, "browsingContext.close new tab", "成功", "新 tab 已关闭"
)
new_tab_ctx = None
except Exception as e:
add_result(
results, "browsingContext.close new tab", "跳过", str(e)[:120]
)
if user_context_id:
page.browser_tools.remove_user_context(user_context_id)
add_result(
results,
"browser.removeUserContext",
"成功",
f"已删除 {user_context_id}",
)
after_remove = page.browser_tools.get_user_contexts()
after_remove_ids = [i.get("userContext") for i in after_remove]
if user_context_id not in after_remove_ids and len(after_remove) == len(
before_ids
):
add_result(
results,
"browser.getUserContexts after remove",
"成功",
f"数量回到 {len(after_remove)}",
)
else:
add_result(
results,
"browser.getUserContexts after remove",
"失败",
"删除后数量或ID校验不通过",
)
user_context_id = None
# 2. client window 状态流
windows = page.browser_tools.get_client_windows()
if not windows:
add_result(
results, "browser.getClientWindows", "失败", "未返回任何 client window"
)
else:
add_result(
results, "browser.getClientWindows", "成功", f"窗口数量: {len(windows)}"
)
win = windows[0]
win_id = win.get("clientWindow")
states = ["minimized", "normal", "maximized", "fullscreen", "normal"]
state_notes = []
state_failed = False
for state in states:
page.browser_tools.set_window_state(win_id, state=state)
time.sleep(0.4)
current_windows = page.browser_tools.get_client_windows()
current = current_windows[0] if current_windows else {}
current_state = current.get("state")
state_notes.append(f"{state}->{current_state}")
if current_state != state:
state_failed = True
if state_failed:
add_result(
results,
"browser.setClientWindowState",
"失败",
", ".join(state_notes),
)
else:
add_result(
results,
"browser.setClientWindowState",
"成功",
", ".join(state_notes),
)
print_results(results)
except Exception as e:
add_result(results, "示例执行", "失败", str(e)[:120])
print_results(results)
raise
finally:
if new_tab_ctx:
try:
page.contexts.close(new_tab_ctx)
except Exception:
pass
if user_context_id:
try:
page.browser_tools.remove_user_context(user_context_id)
except Exception:
pass
try:
page.quit()
except Exception:
pass
if __name__ == "__main__":
main()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""示例25: Browser 模块(用户上下文 + 客户端窗口)严格结果版"""
import io
import sys
import time
from typing import Dict, List
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
from ruyipage import FirefoxPage
def add_result(
results: List[Dict[str, str]], item: str, status: str, note: str
) -> None:
"""记录结构化结果。"""
results.append({"item": item, "status": status, "note": note})
def print_results(results: List[Dict[str, str]]) -> None:
"""打印固定格式结果表。"""
print("\n| 项目 | 状态 | 说明 |")
print("| --- | --- | --- |")
for row in results:
print(f"| {row['item']} | {row['status']} | {row['note']} |")
def main() -> None:
print("=" * 70)
print("测试 25: Browser 用户上下文与窗口管理")
print("=" * 70)
page = FirefoxPage()
results: List[Dict[str, str]] = []
user_context_id = None
new_tab_ctx = None
try:
page.get("https://www.example.com")
# 1. user context 生命周期
before = page.browser_tools.get_user_contexts()
before_ids = [i.get("userContext") for i in before]
add_result(
results,
"browser.getUserContexts before",
"成功",
f"创建前数量: {len(before)}",
)
user_context_id = page.browser_tools.create_user_context()
if user_context_id:
add_result(
results,
"browser.createUserContext",
"成功",
f"userContext={user_context_id}",
)
else:
add_result(
results, "browser.createUserContext", "失败", "未返回 userContext ID"
)
after_create = page.browser_tools.get_user_contexts()
after_create_ids = [i.get("userContext") for i in after_create]
if user_context_id in after_create_ids and len(after_create) == len(before) + 1:
add_result(
results,
"browser.getUserContexts after create",
"成功",
f"数量变为 {len(after_create)}",
)
else:
add_result(
results,
"browser.getUserContexts after create",
"失败",
"创建后数量或ID校验不通过",
)
if user_context_id:
new_tab_ctx = page.browser_tools.create_tab(user_context=user_context_id)
if new_tab_ctx:
add_result(
results,
"browsingContext.create tab in user context",
"成功",
f"context={new_tab_ctx}",
)
else:
add_result(
results,
"browsingContext.create tab in user context",
"失败",
"未返回新 tab context",
)
if new_tab_ctx:
try:
page.contexts.close(new_tab_ctx)
add_result(
results, "browsingContext.close new tab", "成功", "新 tab 已关闭"
)
new_tab_ctx = None
except Exception as e:
add_result(
results, "browsingContext.close new tab", "跳过", str(e)[:120]
)
if user_context_id:
page.browser_tools.remove_user_context(user_context_id)
add_result(
results,
"browser.removeUserContext",
"成功",
f"已删除 {user_context_id}",
)
after_remove = page.browser_tools.get_user_contexts()
after_remove_ids = [i.get("userContext") for i in after_remove]
if user_context_id not in after_remove_ids and len(after_remove) == len(
before_ids
):
add_result(
results,
"browser.getUserContexts after remove",
"成功",
f"数量回到 {len(after_remove)}",
)
else:
add_result(
results,
"browser.getUserContexts after remove",
"失败",
"删除后数量或ID校验不通过",
)
user_context_id = None
# 2. client window 状态流
windows = page.browser_tools.get_client_windows()
if not windows:
add_result(
results, "browser.getClientWindows", "失败", "未返回任何 client window"
)
else:
add_result(
results, "browser.getClientWindows", "成功", f"窗口数量: {len(windows)}"
)
win = windows[0]
win_id = win.get("clientWindow")
states = ["minimized", "normal", "maximized", "fullscreen", "normal"]
state_notes = []
state_failed = False
for state in states:
page.browser_tools.set_window_state(win_id, state=state)
time.sleep(0.4)
current_windows = page.browser_tools.get_client_windows()
current = current_windows[0] if current_windows else {}
current_state = current.get("state")
state_notes.append(f"{state}->{current_state}")
if current_state != state:
state_failed = True
if state_failed:
add_result(
results,
"browser.setClientWindowState",
"失败",
", ".join(state_notes),
)
else:
add_result(
results,
"browser.setClientWindowState",
"成功",
", ".join(state_notes),
)
print_results(results)
except Exception as e:
add_result(results, "示例执行", "失败", str(e)[:120])
print_results(results)
raise
finally:
if new_tab_ctx:
try:
page.contexts.close(new_tab_ctx)
except Exception:
pass
if user_context_id:
try:
page.browser_tools.remove_user_context(user_context_id)
except Exception:
pass
try:
page.quit()
except Exception:
pass
if __name__ == "__main__":
main()
是 browsing context 章节的主参考示例。
适合 browsing-context 页面。
来源:app/content/ruyipage_examples/26_browsing_context_advanced.py · 仓库文件:26_browsing_context_advanced.py
#!/usr/bin/env python
# =============================
# RuyiPage 示例注释版
# 标题: BrowsingContext 高级能力:树、创建、关闭、reload、viewport
# 说明: 是 browsing context 章节的主参考示例。
# 你会学到:
# 1. 获取 browsing context 树
# 2. 通过标准命令创建和关闭 context
# 3. 执行普通 reload 和 ignore_cache reload
# 4. 设置 bypass CSP
# 5. 调整桌面和移动视口尺寸
# 相关 API: page.contexts.get_tree, page.contexts.create_tab, page.contexts.close, page.contexts.reload, page.contexts.set_bypass_csp, page.contexts.set_viewport
# =============================
# -*- coding: utf-8 -*-
"""示例26: BrowsingContext 高级功能(严格结果版)"""
import io
import sys
from typing import Dict, List
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
from ruyipage import FirefoxPage
def add_result(
results: List[Dict[str, str]], item: str, status: str, note: str
) -> None:
"""记录结构化结果。"""
results.append({"item": item, "status": status, "note": note})
def print_results(results: List[Dict[str, str]]) -> None:
"""打印固定格式结果表。"""
print("\n| 项目 | 状态 | 说明 |")
print("| --- | --- | --- |")
for row in results:
print(f"| {row['item']} | {row['status']} | {row['note']} |")
def main() -> None:
print("=" * 70)
print("测试 26: BrowsingContext 高级功能")
print("=" * 70)
page = FirefoxPage()
child_id = None
results: List[Dict[str, str]] = []
try:
page.get("https://www.example.com")
add_result(results, "页面加载", "成功", "example.com 已加载")
tree = page.contexts.get_tree()
contexts = tree.contexts
if contexts:
add_result(
results,
"browsingContext.getTree",
"成功",
f"上下文数量: {len(contexts)}",
)
else:
add_result(results, "browsingContext.getTree", "失败", "未返回任何上下文")
child_id = page.contexts.create_tab()
if child_id:
add_result(
results, "browsingContext.create", "成功", f"新 context: {child_id}"
)
page.contexts.close(child_id)
add_result(results, "browsingContext.close", "成功", "新 context 已关闭")
child_id = None
else:
add_result(results, "browsingContext.create", "失败", "未返回 context ID")
r1 = page.contexts.reload()
if r1.get("navigation"):
add_result(
results,
"browsingContext.reload",
"成功",
f"navigation={r1.get('navigation')}",
)
else:
add_result(
results, "browsingContext.reload", "跳过", "当前返回未包含 navigation"
)
try:
r2 = page.contexts.reload(ignore_cache=True)
add_result(
results,
"browsingContext.reload ignoreCache",
"成功",
f"navigation={r2.get('navigation')}",
)
except Exception as e:
add_result(
results, "browsingContext.reload ignoreCache", "不支持", str(e)[:120]
)
try:
page.contexts.set_bypass_csp(True)
add_result(
results, "browsingContext.setBypassCSP", "成功", "标准命令调用成功"
)
except Exception as e:
add_result(results, "browsingContext.setBypassCSP", "不支持", str(e)[:120])
page.contexts.set_viewport(800, 600)
page.wait(0.2)
vp = page.rect.viewport_size
if tuple(vp) == (800, 600):
add_result(
results,
"browsingContext.setViewport 800x600",
"成功",
f"实际视口: {vp}",
)
else:
add_result(
results,
"browsingContext.setViewport 800x600",
"失败",
f"实际视口: {vp}",
)
page.contexts.set_viewport(375, 667)
page.wait(0.2)
vp2 = page.rect.viewport_size
if tuple(vp2) == (375, 667):
add_result(
results,
"browsingContext.setViewport 375x667",
"成功",
f"实际视口: {vp2}",
)
else:
add_result(
results,
"browsingContext.setViewport 375x667",
"失败",
f"实际视口: {vp2}",
)
print_results(results)
except Exception as e:
add_result(results, "示例执行", "失败", str(e)[:120])
print_results(results)
raise
finally:
if child_id:
try:
page.contexts.close(child_id)
except Exception:
pass
try:
page.quit()
except Exception:
pass
if __name__ == "__main__":
main()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""示例26: BrowsingContext 高级功能(严格结果版)"""
import io
import sys
from typing import Dict, List
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
from ruyipage import FirefoxPage
def add_result(
results: List[Dict[str, str]], item: str, status: str, note: str
) -> None:
"""记录结构化结果。"""
results.append({"item": item, "status": status, "note": note})
def print_results(results: List[Dict[str, str]]) -> None:
"""打印固定格式结果表。"""
print("\n| 项目 | 状态 | 说明 |")
print("| --- | --- | --- |")
for row in results:
print(f"| {row['item']} | {row['status']} | {row['note']} |")
def main() -> None:
print("=" * 70)
print("测试 26: BrowsingContext 高级功能")
print("=" * 70)
page = FirefoxPage()
child_id = None
results: List[Dict[str, str]] = []
try:
page.get("https://www.example.com")
add_result(results, "页面加载", "成功", "example.com 已加载")
tree = page.contexts.get_tree()
contexts = tree.contexts
if contexts:
add_result(
results,
"browsingContext.getTree",
"成功",
f"上下文数量: {len(contexts)}",
)
else:
add_result(results, "browsingContext.getTree", "失败", "未返回任何上下文")
child_id = page.contexts.create_tab()
if child_id:
add_result(
results, "browsingContext.create", "成功", f"新 context: {child_id}"
)
page.contexts.close(child_id)
add_result(results, "browsingContext.close", "成功", "新 context 已关闭")
child_id = None
else:
add_result(results, "browsingContext.create", "失败", "未返回 context ID")
r1 = page.contexts.reload()
if r1.get("navigation"):
add_result(
results,
"browsingContext.reload",
"成功",
f"navigation={r1.get('navigation')}",
)
else:
add_result(
results, "browsingContext.reload", "跳过", "当前返回未包含 navigation"
)
try:
r2 = page.contexts.reload(ignore_cache=True)
add_result(
results,
"browsingContext.reload ignoreCache",
"成功",
f"navigation={r2.get('navigation')}",
)
except Exception as e:
add_result(
results, "browsingContext.reload ignoreCache", "不支持", str(e)[:120]
)
try:
page.contexts.set_bypass_csp(True)
add_result(
results, "browsingContext.setBypassCSP", "成功", "标准命令调用成功"
)
except Exception as e:
add_result(results, "browsingContext.setBypassCSP", "不支持", str(e)[:120])
page.contexts.set_viewport(800, 600)
page.wait(0.2)
vp = page.rect.viewport_size
if tuple(vp) == (800, 600):
add_result(
results,
"browsingContext.setViewport 800x600",
"成功",
f"实际视口: {vp}",
)
else:
add_result(
results,
"browsingContext.setViewport 800x600",
"失败",
f"实际视口: {vp}",
)
page.contexts.set_viewport(375, 667)
page.wait(0.2)
vp2 = page.rect.viewport_size
if tuple(vp2) == (375, 667):
add_result(
results,
"browsingContext.setViewport 375x667",
"成功",
f"实际视口: {vp2}",
)
else:
add_result(
results,
"browsingContext.setViewport 375x667",
"失败",
f"实际视口: {vp2}",
)
print_results(results)
except Exception as e:
add_result(results, "示例执行", "失败", str(e)[:120])
print_results(results)
raise
finally:
if child_id:
try:
page.contexts.close(child_id)
except Exception:
pass
try:
page.quit()
except Exception:
pass
if __name__ == "__main__":
main()
展示单浏览器内三个标签页分别使用不同 user context,实现 Cookie 与存储完全隔离。
适合 user-contexts、browser 和 storage 页面。
适合 Firefox、ADS、FlowerBrowser 等已打开实例的自动探测接管场景。
适合 attach-existing-browser、fingerprint-browser 和 anti-bot 页面。