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 格式