fix: 元素选择器序号语义 + 抓取弹窗直接点击测试
- uiauto_helper: 同属性多实例的选择器由 `//*[@id="x"][k]` 改为 `(//*[@id="x"])[k]`—— 前者在 XPath 里是"父节点内排第 k",多实例时 [2..n] 全部失配(实测抖音底部 4 个同 id tab: 仅 [1] 可用),任务里表现为"未找到元素"但界面上元素明明存在。 - tasks/generic/task.py: 新增 _norm_legacy_xpath,执行前把**历史遗留**的 `//*[@attr=…][k]` 窄范围纠正为带括号形式(只改前缀,结构路径 …/FrameLayout[2] 的 兄弟序号保持不动)——已存任务无需重抓即可恢复。 - editor.js: 抓取弹窗每条元素新增「▶ 点一下」(按 bounds 中心真点一次,/api/screen/tap snap=1 吸附)与「✓ 测选择器」(用将填入的选择器跑 /api/steps/test 验证命中), 点击后自动刷新截图;底部加用法提示。 - doc/TASK_DEV.md:写明 xpath 序号必须整体加括号 + 旧形态自动纠正 + 优先文字/唯一 id。
This commit is contained in:
+15
-7
@@ -89,9 +89,10 @@ def get_elements(serial):
|
||||
1. 有 resource-id/text/content-desc 的元素:
|
||||
先预统计该属性在整棵树中的出现次数——
|
||||
- 唯一出现:直接用属性选择器 //*[@resource-id="x"]
|
||||
- 重复出现(如抖音信息流点赞按钮同 id 几十个):附加 [k] 位置谓词
|
||||
精确到具体实例 //*[@resource-id="x"][k]。uiautomator2 的 d.xpath()
|
||||
底层是 lxml 标准 XPath,[k] 与抓取时同一语义,不会误中屏幕外第一个。
|
||||
- 重复出现(如抖音底部导航 4 个 tab 同 id):用**整体加括号**的
|
||||
`(//*[@resource-id="x"])[k]` 精确到第 k 个匹配。
|
||||
注意 XPath 语义:`//*[@id="x"][k]` 是"在其父节点中排第 k",不是
|
||||
第 k 个匹配——历史实现踩过这个坑(多实例时 [2..n] 全部失配)。
|
||||
2. 无任何属性的元素:
|
||||
用最近一个有属性祖先的选择器限定范围 + 同 class 兄弟序号定位
|
||||
(如 //*[@resource-id="x"]/FrameLayout/ImageView[2]);
|
||||
@@ -153,14 +154,21 @@ def _extract(root, out):
|
||||
count_attrs(c)
|
||||
|
||||
def attr_selector(attr, value, cnt, seen):
|
||||
"""属性选择器:唯一直接出,重复加 [k] 位置谓词。返回 (选择器, suggested)。"""
|
||||
"""属性选择器:唯一直接出;重复时用 `(…)[k]` 精确到**第 k 个匹配**。
|
||||
|
||||
重要(XPath 位置谓词语义):
|
||||
//*[@resource-id="x"][2] → 「在**其父节点**中排第 2 的属性匹配」,**不是**第 2 个匹配
|
||||
(//*[@resource-id="x"])[2] → 「第 2 个匹配」← 我们要的
|
||||
历史实现写成前者,导致同 id 多实例(如抖音底部导航 4 个 tab 同 id)时
|
||||
[2..n] 全部匹配不到 → 运行时"未找到元素"(2026-09-10 实测修复)。
|
||||
"""
|
||||
seen[value] += 1
|
||||
val = f'//*[@{attr}={_xpath_q(value)}]'
|
||||
base = f'//*[@{attr}={_xpath_q(value)}]'
|
||||
if cnt[value] > 1:
|
||||
val += f"[{seen[value]}]"
|
||||
val = f"({base})[{seen[value]}]"
|
||||
return val, {"type": "xpath", "value": val,
|
||||
"indexed": True, "occ": seen[value], "total": cnt[value]}
|
||||
return val, {"type": "xpath", "value": val}
|
||||
return base, {"type": "xpath", "value": base}
|
||||
|
||||
def flatten(node, depth=0, path="", ctx=None, tag_path="", tag_index=1):
|
||||
"""递归扁平化。
|
||||
|
||||
Reference in New Issue
Block a user