全球动态代理 · 静态住宅代理 · 极高质量 · 包月不限量 · 学员低价 · 联系如意进行免费测试 · 欢迎公司及学员采购 · 另有任何海外数据采集需求,随时联系 · 商务合作微信:Charleval
Automation / 表单能力
RuyiPage Docs

RuyiPage 表单能力

表单操作包括输入、选择、文件上传和表单提交。掌握 input/click/select 三个核心方法即可覆盖大部分场景。

核心 API

ele.select

返回: None
ele.select(value=None, text=None, index=None)

选择 <select> 下拉框的选项。支持按 value/text/index 选择。

参数说明
参数说明
valueoption 的 value 属性值。
textoption 的显示文本。
indexoption 的索引(从 0 开始)。
常见坑
  • 自定义下拉菜单(非原生 select)需要用 click + ele 组合定位。
相关示例

page.actions.press

返回: Actions
page.actions.press(key).perform()

按键操作,常用于表单提交(Enter)、Tab 切换焦点。

参数说明
参数说明
keyKeys 枚举值,如 Keys.ENTER、Keys.TAB。
常见坑
  • perform() 必须调用,否则按键不会执行。
相关示例

其他表单方法

API说明
ele.clear()清空输入框
ele.attr('value')读取输入框当前值
ele.attr('checked')读取复选框状态
page.run_js('form.submit()')JS 提交表单

快速上手

python 搜索表单操作
from ruyipage import launch, Keys

page = launch()
try:
    page.get("https://cn.bing.com/")

    # 找到搜索框并输入
    search_box = page.ele("#sb_form_q")
    search_box.input("小肩膀教育", clear=True)

    # 按 Enter 提交
    page.actions.press(Keys.ENTER).perform()
    page.wait(3)

    print(f"搜索结果页: {page.title}")
finally:
    page.quit()

下拉框选择示例

python 原生 select 操作
# 按显示文本选择
select_ele = page.ele("css:select#country")
select_ele.select(text="中国")

# 按 value 选择
select_ele.select(value="CN")

# 按索引选择
select_ele.select(index=0)  # 选第一项

相关示例源码

3 个示例 ▶ 点击展开

元素交互:点击、输入、选择、悬停、表单提交

examples/03_element_interaction.py

围绕元素对象的高层交互,覆盖输入、点击、选择和状态读取。

GitHub 仓库 查看仓库源码 click_selfinputclearvalueselect.by_valuedouble_clickright_clickhover
  • 点击按钮并读取页面反馈
  • 填写文本、邮箱、密码和数字输入框
  • 清空后重新输入
  • 切换复选框和单选框
  • 在原生模式与兼容模式下操作 select
  • 执行双击、右击和悬停

源码文件:app/content/ruyipage_examples/03_element_interaction.py · 仓库:03_element_interaction.py

python 注释版
# =============================
# RuyiPage 示例注释版
# 标题: 元素交互:点击、输入、选择、悬停、表单提交
# 说明: 围绕元素对象的高层交互,覆盖输入、点击、选择和状态读取。
# 你会学到:
#   1. 点击按钮并读取页面反馈
#   2. 填写文本、邮箱、密码和数字输入框
#   3. 清空后重新输入
#   4. 切换复选框和单选框
#   5. 在原生模式与兼容模式下操作 select
# 相关 API: click_self, input, clear, value, select.by_value, double_click, right_click, hover
# =============================

# -*- coding: utf-8 -*-
"""
示例3: 元素交互
测试功能:
- 点击元素
- 输入文本
- 清空输入
- 双击、右键点击
- 鼠标悬停
- 拖拽
"""

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_element_interaction():
    """测试元素交互功能"""
    print("=" * 60)
    print("测试3: 元素交互")
    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. 点击按钮:")
        click_btn = page.ele("#click-btn")
        click_btn.click_self()
        page.wait(0.5)
        result = page.ele("#click-result").text
        print(f"   点击结果: {result}")

        # 再点击几次
        click_btn.click_self()
        click_btn.click_self()
        page.wait(0.5)
        result = page.ele("#click-result").text
        print(f"   多次点击后: {result}")

        # 2. 输入文本
        print("\n2. 输入文本:")
        text_input = page.ele("#text-input")
        text_input.input("Hello RuyiPage!")
        page.wait(0.5)
        print(f"   输入的值: {text_input.value}")

        # 3. 清空并重新输入
        print("\n3. 清空并重新输入:")
        text_input.clear()
        text_input.input("新的文本内容")
        print(f"   新的值: {text_input.value}")

        # 4. 输入到其他类型的输入框
        print("\n4. 输入到不同类型的输入框:")
        page.ele("#email-input").input("[email protected]")
        page.ele("#password-input").input("password123")
        page.ele("#number-input").input("42")
        page.ele("#textarea").input("这是多行文本\n第二行\n第三行")
        print(f"   ✓ 所有输入框填写完成")

        # 5. 复选框操作
        print("\n5. 复选框操作:")
        checkbox1 = page.ele("#checkbox1")
        checkbox2 = page.ele("#checkbox2")
        print(f"   checkbox1初始状态: {checkbox1.is_checked}")
        checkbox1.click_self()
        page.wait(0.3)
        print(f"   checkbox1点击后: {checkbox1.is_checked}")
        checkbox2.click_self()
        page.wait(0.3)
        print(f"   checkbox2点击后: {checkbox2.is_checked}")

        # 6. 单选框操作
        print("\n6. 单选框操作:")
        radio1 = page.ele("#radio1")
        radio2 = page.ele("#radio2")
        radio1.click_self()
        page.wait(0.3)
        print(f"   radio1选中: {radio1.is_checked}")
        radio2.click_self()
        page.wait(0.3)
        print(f"   radio2选中: {radio2.is_checked}")
        print(f"   radio1现在: {radio1.is_checked}")

        # 7. 下拉选择
        print("\n7. 下拉选择:")
        select = page.ele("#select-single")
        # 教学建议:优先 native_only,失败再切 compat。
        select_ok = select.select.by_value("opt2", mode="native_only")
        if not select_ok:
            print("   native_only 失败,切换到 compat 模式保底...")
            select_ok = select.select.by_value("opt2", mode="compat")
        page.wait(0.5)
        print(f"   选中的值: {select.value}")
        selected = select.select.selected_option
        print(f"   选中的文本: {selected['text']}")
        print(f"   原生选择结果: {select_ok}")

        # 8. 双击
        print("\n8. 双击按钮:")
        dbl_btn = page.ele("#double-click-btn")
        dbl_btn.double_click()
        page.wait(0.5)
        result = page.ele("#click-result").text
        print(f"   双击结果: {result}")

        # 9. 右键点击
        print("\n9. 右键点击:")
        right_btn = page.ele("#right-click-btn")
        right_btn.right_click()
        page.wait(0.5)
        result = page.ele("#click-result").text
        print(f"   右键点击结果: {result}")

        # 10. 鼠标悬停
        print("\n10. 鼠标悬停:")
        hover_target = page.ele("#hover-target")
        hover_target.hover()
        page.wait(0.5)
        hover_result = page.ele("#hover-result").text
        print(f"   悬停结果: {hover_result}")

        # 11. 提交表单
        print("\n11. 提交表单:")
        submit_btn = page.ele("#submit-btn")
        submit_btn.click_self()
        page.wait(0.5)
        form_result = page.ele("#form-result").text
        print(f"   表单结果: {form_result[:50]}...")

        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_element_interaction()

Script 与 Input 高级能力:realms、handle 回收、文件输入

examples/29_script_input_advanced.py

连接 script 与 input 的高级能力,是 script 页的重要支撑示例。

GitHub 仓库 查看仓库源码 page.get_realmspage.eval_handlepage.disown_handlesele.input
  • 获取全部和 window 类型 realms
  • 执行脚本得到远程对象句柄
  • 回收单个和批量 handle
  • 利用 file input 上传单文件和多文件

源码文件:app/content/ruyipage_examples/29_script_input_advanced.py · 仓库:29_script_input_advanced.py

python 注释版
#!/usr/bin/env python
# =============================
# RuyiPage 示例注释版
# 标题: Script 与 Input 高级能力:realms、handle 回收、文件输入
# 说明: 连接 script 与 input 的高级能力,是 script 页的重要支撑示例。
# 你会学到:
#   1. 获取全部和 window 类型 realms
#   2. 执行脚本得到远程对象句柄
#   3. 回收单个和批量 handle
#   4. 利用 file input 上传单文件和多文件
# 相关 API: page.get_realms, page.eval_handle, page.disown_handles, ele.input
# =============================

# -*- coding: utf-8 -*-
"""示例29: Script + Input 高级能力(严格结果版)"""

import io
import os
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, ScriptResult


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("测试 29: Script + Input 高级能力")
    print("=" * 70)

    page = FirefoxPage()
    results: List[Dict[str, str]] = []
    html_path = os.path.join(os.path.dirname(__file__), "test_file_input.html")
    file1 = os.path.join(os.path.dirname(__file__), "test1.txt")
    file2 = os.path.join(os.path.dirname(__file__), "test2.txt")

    try:
        page.get("https://www.example.com")
        add_result(results, "页面加载", "成功", "example.com 已加载")

        # 1. script.getRealms
        realms = page.get_realms()
        if realms:
            add_result(
                results, "script.getRealms all", "成功", f"realm 数量: {len(realms)}"
            )
        else:
            add_result(results, "script.getRealms all", "失败", "未返回任何 realm")

        window_realms = page.get_realms(type_="window")
        if window_realms:
            add_result(
                results,
                "script.getRealms window",
                "成功",
                f"window realm 数量: {len(window_realms)}",
            )
        else:
            add_result(
                results, "script.getRealms window", "失败", "未返回 window realm"
            )

        # 2. script.disown
        single: ScriptResult = page.eval_handle("({data: 'test', array: [1, 2, 3]})")
        if single.success and single.result.handle:
            page.disown_handles([single.result.handle])
            add_result(
                results,
                "script.disown single",
                "成功",
                f"handle={single.result.handle}",
            )
        else:
            add_result(results, "script.disown single", "跳过", "脚本结果未返回 handle")

        handles = []
        for i in range(3):
            item = page.eval_handle(f"({{id: {i}, value: 'test{i}'}})")
            if item.success and item.result.handle:
                handles.append(item.result.handle)
        if handles:
            page.disown_handles(handles)
            add_result(
                results, "script.disown batch", "成功", f"句柄数量: {len(handles)}"
            )
        else:
            add_result(results, "script.disown batch", "跳过", "未拿到可用 handle")

        # 3. input.setFiles 通过高层文件输入
        html_content = """
        <!DOCTYPE html>
        <html>
        <head><meta charset='utf-8'><title>File Input Test</title></head>
        <body>
            <input type='file' id='single-file' />
            <input type='file' id='multiple-files' multiple />
            <div id='result'></div>
            <script>
                document.getElementById('single-file').addEventListener('change', function(e) {
                    document.getElementById('result').textContent = 'Single file: ' + e.target.files[0].name;
                });
                document.getElementById('multiple-files').addEventListener('change', function(e) {
                    document.getElementById('result').textContent = 'Multiple files: ' + e.target.files.length;
                });
            </script>
        </body>
        </html>
        """

        with open(html_path, "w", encoding="utf-8") as f:
            f.write(html_content)
        with open(file1, "w", encoding="utf-8") as f:
            f.write("Test file 1 content")
        with open(file2, "w", encoding="utf-8") as f:
            f.write("Test file 2 content")

        page.get(f"file:///{html_path.replace(os.sep, '/')}")
        add_result(results, "文件测试页加载", "成功", "本地 file input 页面已加载")

        page.ele("#single-file").input(file1)
        single_text = page.ele("#result").text
        if "test1.txt" in single_text:
            add_result(results, "input.setFiles single", "成功", single_text)
        else:
            add_result(results, "input.setFiles single", "失败", single_text)

        page.ele("#multiple-files").input([file1, file2])
        multi_text = page.ele("#result").text
        if "Multiple files: 2" in multi_text:
            add_result(results, "input.setFiles multiple", "成功", multi_text)
        else:
            add_result(results, "input.setFiles multiple", "失败", multi_text)

        print_results(results)

    except Exception as e:
        add_result(results, "示例执行", "失败", str(e)[:120])
        print_results(results)
        raise
    finally:
        for path in (file1, file2, html_path):
            try:
                if os.path.exists(path):
                    os.remove(path)
            except Exception:
                pass
        try:
            page.quit()
        except Exception:
            pass


if __name__ == "__main__":
    main()

原生 Select 选择:只测 native_only 模式与事件诊断

examples/36_native_bidi_select.py

专门帮助排查 select 的焦点、键盘与 trusted change 路径问题。

GitHub 仓库 查看仓库源码 select.by_valueselect.selected_optionclick_selfpage.actions.key_downpage.actions.key_uppage.run_js
  • 检查 select 初始状态
  • 聚焦后通过键盘切换选项
  • 强制只走 native_only 模式
  • 读取 changeCount 和 trustedChange 诊断信息

源码文件:app/content/ruyipage_examples/36_native_bidi_select.py · 仓库:36_native_bidi_select.py

python 注释版
# =============================
# RuyiPage 示例注释版
# 标题: 原生 Select 选择:只测 native_only 模式与事件诊断
# 说明: 专门帮助排查 select 的焦点、键盘与 trusted change 路径问题。
# 你会学到:
#   1. 检查 select 初始状态
#   2. 聚焦后通过键盘切换选项
#   3. 强制只走 native_only 模式
#   4. 读取 changeCount 和 trustedChange 诊断信息
# 相关 API: select.by_value, select.selected_option, click_self, page.actions.key_down, page.actions.key_up, page.run_js
# =============================

# -*- coding: utf-8 -*-
"""
示例36: 原生 BiDi Select 检测(仅原生)

目标:
1) 只测试 native_only(不允许 JS 保底)
2) 输出分步诊断信息,帮助定位焦点/键盘路径问题
"""

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, Keys


def _state(page, select):
    s = page.run_js("return window.nativeBidiSelectState")
    return {
        "value": select.value,
        "selected_option": select.select.selected_option,
        "focused": s.get("focused"),
        "trusted_change": s.get("trustedChange"),
        "change_count": s.get("changeCount"),
        "events": s.get("events", []),
    }


def test_native_bidi_select():
    print("=" * 60)
    print("测试36: 原生 BiDi Select(仅原生)")
    print("=" * 60)

    opts = FirefoxOptions()
    opts.headless(False)
    page = FirefoxPage(opts)

    try:
        test_page = os.path.join(
            os.path.dirname(__file__), "test_pages", "native_bidi_select_test.html"
        )
        test_url = "file:///" + os.path.abspath(test_page).replace("\\", "/")
        page.get(test_url)
        page.wait(1)

        select = page.ele("#single-select")

        print("\n1. 初始状态:")
        st = _state(page, select)
        print(f"   value: {st['value']}")
        print(f"   selected_option: {st['selected_option']}")
        print(f"   focused: {st['focused']}")

        print("\n2. 手动焦点探测(原生 click + 一步键盘):")
        select.click_self()
        page.wait(0.1)
        page.actions.key_down(Keys.DOWN).key_up(Keys.DOWN).perform()
        page.wait(0.1)
        st_probe = _state(page, select)
        print(f"   probe value: {st_probe['value']}")
        print(f"   probe focused: {st_probe['focused']}")
        print(f"   probe changeCount: {st_probe['change_count']}")

        print("\n3. 使用 selector native_only 选择 opt2:")
        ok = select.select.by_value("opt2", mode="native_only")
        page.wait(0.3)
        st = _state(page, select)
        print(f"   native_only结果: {ok}")
        print(f"   value: {st['value']}")
        print(f"   selected_option: {st['selected_option']}")
        print(f"   focused: {st['focused']}")
        print(f"   trustedChange: {st['trusted_change']}")
        print(f"   changeCount: {st['change_count']}")

        print("\n4. 页面事件日志:")
        for line in st["events"]:
            print(f"   {line}")

        if not ok or select.value != "opt2":
            raise RuntimeError("native_only 未成功切换到 opt2")

        print("\n" + "=" * 60)
        print("✓ 原生 BiDi Select 测试通过!")
        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_native_bidi_select()