aardio 文档

aardio 范例: web.view XPath 自动化教学范例

waitEle 范例

import win.ui;
/*DSG{{*/
var winform = win.form(text="web.view XPath 自动化教学范例";right=850;bottom=650)
winform.add()
/*}}*/

import web.view;
var wb = web.view(winform);

var markdown = /*
在 aardio 的 web.view 中,
利用 XPath 来实现自动化控制是非常强大且灵活的。

wb.waitEle 和 wb.waitEle2 函数原生支持 XPath,
**只要参数 1 指定的字符串以 / 或 ( 符号开头,就会被识别为 XPath 表达式**。

相较于 CSS 选择器,XPath 最大的优势在于**它可以通过元素的文本内容来定位元素**,
这在很多动态生成不规则 class 或缺少 id 属性的现代网页中是必杀技。

### 🎯 XPath 常用语法速查:

1. **`//` 和 `/` 的区别**

   - `//div`:查找文档中**任意层级**的所有 `div`。
   - `/html/body/div`:从根节点**严格按层级**查找。通常我们直接用 `//` 相对查找。

   例如 `//div[h3[contains(., '改进模型')]]//button`:
   找一个内部包含标题为"改进模型"的 `div`,然后再找它里面的 `button`。
   这在一些无特征但结构固定的复杂列表中非常有效。

2. **`[@属性名='值']` 限定属性**

   - `//input[@type="password"]`:找密码框。
   - `//a[@href="https://www.aardio.com"]`:找指定链接。

3.  XPath 函数

    常用 XPath 函数:contains()、normalize-space()、starts-with()、substring()

    函数 `contains(haystack, needle)` 
    接收参数(通常是两个字符串),执行逻辑判断,并返回一个布尔值(True 或 False)。
    参数名源自“Looking for a needle in a haystack”(在草垛中寻针,大海捞针)。

    **部分匹配 CSS 类名示例:**

    `div[contains(@class,"class-name")]`

    HTML 元素可能会有动态绑定的 CSS 类名,一般不要用 `=` 号做完全匹配。


    **部分匹配文本示例:**

    - `[contains(., "包含文本")]` 模糊匹配当前节点与任意深度子节点的文本(最精妙的用法)  
    - `[contains(text(), "包含文本")]` 模糊匹配当前节点文本(最精妙的用法) 

    以上参数中的 `text()` 不是函数而是一个节点测试(Node Test)。
    text 后面的 `()`表示它是一个“测试”动作,以区别于普通的元素名。

    类似的测试器:

    - node():匹配任何类型的节点。
    - comment():匹配注释节点。
    - processing-instruction():匹配处理指令节点。

    **用参数 `.` 替代 `text()`取文本的作用**  
    如果 HTML 是 `<button><span>登录</span></button>`,
    使用 `text()` 可能匹配不到外层的 button,
    而 `.` 会将该节点内部所有子节点的文本拼在一起来匹配,**适用性更广**。

4. **逻辑组合**

   - 用 and 表示逻辑与关系。  
   例如 data-role="属性值" 并且包含指定字符串:  
   `//button[@data-role="属性值" and contains(., "包含字符串")]`

   - 用 or 表示逻辑或关系。  
   例如同时兼容中英文:  
   `//button[contains(., "Login") or contains(., "登录")]`

5. **轴(Axes)**

    轴(Axes) 定义了相对于“当前节点”的查找方向。
    如果把 HTML 文档看作一棵“家谱树”,XPath 的步进(Step)就像是在树上移动。
    普通的 / 或 // 只能向下查找(后代),
    而“轴”允许你横向查找(兄弟)、向上查找(祖先)或按文档顺序前后查找。

    - following-sibling:: (同级后续轴)
    仅查找与当前节点同一个父节点下的“兄弟”节点。

        示例:`//div[text()='弹窗标题']/following::button[.//span[text()='取消']]`

    - following:: (后续轴)
    查找当前节点结束标签之后的所有节点(不包括其后代),即文档流中出现在它后面的任何元素。
    也就是所有在后面的“兄弟”节点以及这些“兄弟”节点的任意深度的子节点。

        示例:`//div[contains(., '弹窗标题')]/following-sibling::div//button[contains(., '取消')]`

5. **绕过命名空间**

    XPath 默认不识别有特殊 XML 命名空间的元素,
    例如用 svg 作为标签名就找不到节点,需要改用 local-name() 函数来绕过命名空间限制。

    示例:`//*[local-name()='svg' and @name='Refresh']]`
*/

wb.html = /**
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>XPath 自动化教学</title>
<style>
body { font-family: "Microsoft YaHei", sans-serif; padding: 5px; line-height: 1.5; }
.test-box { border: 1px solid #0078D7; padding: 15px; margin: 15px 0; border-radius: 5px; background: #f3f9ff; }
h3 { margin-top: 0; color: #0078D7; }
code { background: #e0e0e0; padding: 2px 5px; border-radius: 3px; color: #d14; }
button { padding: 5px 15px; cursor: pointer; }
</style></head>
<body>
    <p>在 aardio 中,如果 <code>wb.waitEle</code> 的选择器参数以 <code>/</code> 开头,则被识别为 XPath。XPath 可以根据节点路径、属性、乃至<b>包含的文本内容</b>来精确定位元素。</p>

    <div class="test-box">
        <h3>测试 1:通过绝对或相对路径及属性查找</h3>
        <p>XPath 示例:<code>//input[@id="demo-input"]</code></p>
        <input type="text" id="demo-input" placeholder="等待 aardio 自动填入文本..." size="50">
    </div>

    <div class="test-box">
        <h3>测试 2:通过文本内容查找(CSS选择器做不到)</h3>
        <p>XPath 示例:<code>//button[contains(., '自动点击我')]</code> 或 <code>//button[text()='精确匹配文本']</code></p>
        <button>普通的按钮</button>
        <button>自动点击我</button>
        <button disabled id="markdown">点我显示:XPath 常用语法速查</button>
    </div>

    <div class="test-box">
        <h3>测试 3:多条件组合与层级查找</h3>
        <p>XPath 示例:<code>//div[@class="actions"]//button[@data-role="submit"]</code></p>
        <div class="actions">
            <button data-role="cancel">取消</button>
            <button data-role="submit">提交数据</button>
        </div>
    </div>

    <h3 id="msg" style="color:#d14;">操作日志:...</h3>

    <script>
        // 绑定所有按钮的点击事件,用于反馈测试结果
        document.querySelectorAll('button').forEach(btn => {
            btn.onclick = function() {
                var msg = document.getElementById('msg');
                msg.innerText = "操作日志:你点击了按钮「" + this.innerText + "」";
                this.style.backgroundColor = "#c8e6c9";
            }
        });
    </script>
</body>
</html>
**/

winform.show();

/*
参数 1 首字符是 `/` 表示 XPath ,否则表示 CSS 选择器。
waitEle 范例: https://www.aardio.com/zh-cn/doc/example/WebUI/web.view/waitEle.html

在 XPath 中,@ 符号是轴选择器(Axis Selector),用于选取属性(Attribute)。
以下示例表示:寻找一个 input 标签,它的 id 属性值等于 "demo-input" 。
*/
wb.waitEle(`//input[@id="demo-input"]`, `
    // 这里的 this 指向查找到的 HTML 节点
    this.value = 'Hello, aardio + XPath!';
`);// waitEle 在当前页面等待节点,改用 waitEle2 方法则支持跨页面等待节点

/*
以上 XPath 去掉 @ 则表示:
寻找一个 input 标签,该标签包含一个 <span> 子标签,并且子标签的文本内容是 "demo-input"。
*/
wb.waitEle(`//input[span="demo-input"]`,  //一般用反引号 `` 包围 XPath,内部可直接写双引号
    100 //省略参数 2 或指定超时值则同步等待(阻塞代码向下执行)
); 

/*
1. 完整匹配文本:`//button[text()="精确匹配文本"]`
2. 部分匹配文本:`//button[contains(text(), '包含的字符串')]`
3. 节点或子节点包含文本:`//button[contains(., '包含的字符串')]` 

    参数 `.` 会将该节点内部所有子节点的文本拼在一起来匹配。

4. 去掉首尾空白压缩中间空白://button[div[normalize-space()='清洗并规范化空白后的内容']]
*/
wb.waitEle(`//button[contains(., '自动点击我')]`, `
   this.click();
`);//参数 2 可指定异步(不阻塞代码向下执行)回调的 JavaScript 或 aardio 函数


/*
XPath 用斜杠 `/` 分隔上下级表示包含关系。
最开始的单个斜杆表示根元素,中间的单斜杆分隔父子关系。
而两个斜杠 `//` 表示模糊匹配任意深度的路径(任意深度子孙节点)。
可以用 `/..` 表示寻找父节点,例如 `//button[contains(., "提交")]/..`

下面的 `div[@class="actions"]` 先找到指定 div,
再用 `//button` 寻找它内部深层的指定按钮
*/
wb.waitEle(`//div[@class="actions"]//button[@data-role="submit"]`, `
    setTimeout(() => {this.click();document.getElementById("markdown").disabled = false}, 500);
`);

wb.waitEle(`//h3[@id="msg"][contains(text(),"常用语法速查")]`
    , function(){
        //先退出当前网页回调,用 setTimeout 异步替换网页
        winform.setTimeout( 
            function(){
                import string.markdown;
                var html = string.markdown().render(markdown);
                wb.html= "<html><head><style>" + $"~\doc\style.css" + "</style></head><body>" + html;   
            }
        );
    }
);  

win.loopMessage();
Markdown 格式