App 自动化高级定位技巧(XPath + UiAutomator)(十二)
一、XPath 定位进阶(核心:节点关系定位)
XPath 是 XML 路径语言,可通过元素的层级、属性、关系定位 App 界面元素(Android 界面本质是 XML 结构)。核心特点:路径灵活、支持层级关联、节点关系遍历。
1. XPath 基础语法回顾
|
|
|
|
// |
|
//*[@resource-id='xxx'] |
/ |
|
父节点/子节点 |
* |
|
//*
|
@ |
|
@resource-id
|
[] |
|
TextView[2]
|
.. |
|
子节点/..
|
2. 核心层级定位场景(实战核心)
(1)父节点定位子节点(最常用)
场景:父元素属性唯一(如 resource-id),子元素无唯一属性但位置 / 类型确定。
逻辑:先定位唯一父节点 → 再通过子节点的类型 / 索引定位。
//*[@resource-id='com.xueqiu.android:id/title_container']/android.widget.TextView[2]
① //*[@resource-id=’com.xueqiu.android:id/title_container’]:找到页面上 resource-id 为title_container的所有元素(父节点);
② /android.widget.TextView[2]:在该父节点的直接子节点中,选取第 2 个 class 为android.widget.TextView的元素
(2)子节点定位父节点
场景:子元素易定位(如 text 唯一),但父元素无唯一属性,需通过子找父。
//*[@resource-id='com.xueqiu.android:id/title_container']/android.widget.TextView[2]/..
解读:先找到上述子节点 → 通过/..定位到该子节点的直接父节点(即title_container)。
扩展:如需找爷爷节点,可叠加/.. → 子节点/../../..(3 个/..表示向上找 3 级父节点)。
(3)子节点定位兄弟节点
场景:两个元素为同一父节点下的兄弟,其中一个易定位,需找另一个。
示例:找到 “09988” 的兄弟节点 “涨跌幅”
//*[@text='09988']/../android.widget.TextView[3]
(4)跨级定位(爷爷→孙子 / 任意后代)
场景:目标元素与祖先元素有明确层级,但非直接父子。
核心:通过/逐级向下,或//跳过中间层级(匹配任意后代)。
实战案例:验证 “09988” 和 “73.25” 的关联性(是否属于同一父容器)
//*[@text='09988']/../../..//*[@resource-id='com.xueqiu.android:id/current_price']
① //*[@text=’09988′]:定位文本为 “09988” 的元素(孙子节点);
② /../../..:向上找 3 级父节点(找到两者的共同祖先);
③ //*[@resource-id=’current_price’]:在共同祖先的所有后代中,找到价格元素(73.25);
④ 作用:验证两个元素是否属于同一模块(若能定位到,说明关联性成立)。
3. XPath 定位注意事项
优先用resource-id/text等稳定属性,避免仅用 class + 索引(易因界面变化失效);
层级不宜过长(如/../../../../),否则易因界面结构变动导致定位失败。
二、UiAutomator 定位(Android 专属,效率更高)
UiAutomator 是 Android 官方提供的 UI 自动化测试框架,Appium 封装了该接口,定位效率高于 XPath(直接调用 Android 底层 API),支持更灵活的组合定位。
1. 基础用法
from appium.webdriver.common.appiumby import AppiumBy# 核心格式:通过By.ANDROID_UIAUTOMATOR传入UiAutomator表达式driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'UiAutomator表达式').click()
2. 单独定位(单一属性匹配)
适用于元素有唯一属性的场景,语法简洁,优先级:resourceId > text > className > description。
|
|
|
|
|
|
new UiSelector().resourceId("完整id") |
new UiSelector().resourceId("com.xueqiu.android:id/login_account") |
|
|
new UiSelector().className("类名") |
new UiSelector().className("android.widget.TextView") |
|
|
new UiSelector().description("desc文本") |
new UiSelector().description("返回") |
|
|
new UiSelector().text("完整文本") |
new UiSelector().text("我的") |
|
|
new UiSelector().textContains("文本片段") |
new UiSelector().textContains("帐号密码") |
|
|
new UiSelector().textStartsWith("开头文本") |
new UiSelector().textStartsWith("登录") |
|
|
new UiSelector().textMatches("正则表达式") |
new UiSelector().textMatches("^[0-9]{5}$")
|
# 1. 点击“我的”(精准文本)driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("我的")').click()# 2. 输入账号(通过resource-id)driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().resourceId("com.xueqiu.android:id/login_account")').send_keys("long")# 3. 模糊匹配“帐号密码”driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().textContains("帐号密码")').click()
3. 组合定位(多属性 / 层级关联,解决元素重复问题)
适用于单一属性无法定位(如多个 “我的” 按钮),需通过多属性 / 层级缩小范围。
(1)多属性组合(id+text / 类名 + text)
场景:页面有多个相同 text 的元素,需结合 resource-id 区分。
语法:resourceId(“id”).text(“文本”)(多个属性直接拼接)
实战示例:定位雪球 App 的 “我的” 标签(避免其他 “我的” 干扰)
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().resourceId("com.xueqiu.android:id/tab_name").text("我的")').click()
(2)父子关系定位(childSelector)
场景:子元素无唯一属性,但父元素易定位(如父节点有唯一 resource-id)。
语法:父元素定位.childSelector(子元素定位)
实战示例:通过父容器title_container找 “股票” 文本子元素
# 注意:原示例中test('股票')是笔误,应为text('股票')driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().resourceId("com.xueqiu.android:id/title_container").childSelector(text("股票"))').click()
(3)兄弟关系定位(fromParent)
场景:目标元素难定位,但同属一个父节点的兄弟元素易定位。
逻辑:先定位兄弟元素 → 通过fromParent找到同一父节点下的目标元素。
语法:兄弟元素定位.fromParent(目标元素定位)
实战示例:通过title_container兄弟元素找 “用户” 文本元素
# 注意:原示例中test('用户')是笔误,应为text('用户')driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().resourceId("com.xueqiu.android:id/title_container").fromParent(text("用户"))').click()
(4)滚动查找元素(UiScrollable)
场景:目标元素在屏幕外,需滚动页面才能显示(如列表、长页面)。
核心:UiScrollable(滚动容器) + scrollIntoView(滚动到目标元素可见)
new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text("查找的文本").instance(0))
# Python代码示例scroll_expression = '''new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text("设置").instance(0))'''driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, scroll_expression).click()
scrollable(true):筛选可滚动的容器;
instance(0):选取第 1 个可滚动容器(页面通常只有 1 个主滚动容器);
scrollIntoView:滚动直到目标元素可见。
三、XPath vs UiAutomator 对比与选型建议
简单场景(单一属性 / 父找子):优先 XPath(语法简洁);
复杂场景(滚动、重复元素、兄弟定位):优先 UiAutomator(效率 + 稳定性更高);
跨平台(Android+iOS):优先 XPath(UiAutomator 仅支持 Android)。
总结
XPath 核心:通过/////..实现层级遍历,利用@属性+[索引]定位,核心解决 “节点上下层级关联” 问题(如子找父、跨级找后代);
UiAutomator 核心:支持单一属性、多属性组合、父子 / 兄弟关联、滚动查找,是 Android 端更高效的定位方式;
实战关键:优先用稳定属性(resource-id > text > class),减少层级依赖,避免因界面改版导致定位失效。