3、元素定位(下):XPath定位与CSS Selector定位
好,咱们接着聊元素定位。上一章我们把基础定位方法过了一遍,这一章要深入两个真正有杀伤力的定位方式——XPath和CSS Selector。说实话,这两个才是自动化测试里的重头戏。我个人习惯是,能用CSS Selector就用CSS,搞不定的再上XPath。为什么?后面我会细说。
一、XPath定位:绝对路径与相对路径
XPath这东西,说白了就是给HTML文档画一张“地图”。你可以用绝对路径,也可以用相对路径来找元素。
1. 绝对路径
绝对路径从根节点/html开始,一层层往下写。比如:
/html/body/div[2]/div[1]/form/input[3]
嗯,这里要注意——这种写法极其脆弱。我在项目中遇到过,前端同事只是加了一个div,整个定位就全崩了。你想想看,页面结构稍微一变,你的脚本就得重写。所以,绝对路径能不用就别用。
2. 相对路径
相对路径用双斜杠//开头,表示从任意位置开始查找。这才是我们日常用的主力:
//input[@id='username']
//div[@class='login-box']//button
相对路径灵活多了。你只要找到元素的一个特征,就能定位到它。我个人习惯是,优先用id或name属性,没有的话再用class或其他属性组合。
二、XPath轴:让你在DOM树里“横着走”
XPath轴是个好东西,但很多人不太会用。它让你可以在DOM树里上下左右自由穿梭。比如你想找一个元素,但它本身没什么特征,你可以通过它的兄弟节点或父节点来定位。
常用的轴有这些:
| 轴名称 | 含义 | 示例 |
|---|---|---|
| ancestor | 所有祖先节点 | //span/ancestor::div |
| following-sibling | 后面的兄弟节点 | //h2/following-sibling::p |
| preceding-sibling | 前面的兄弟节点 | //p/preceding-sibling::h2 |
| child | 直接子节点 | //div/child::input |
| parent | 父节点 | //input/parent::form |
举个例子。假设页面上有个表格,你想定位第三行第二列的元素,但那个元素没有id也没有class。怎么办?你可以用轴:
//table[@id='data-table']/tbody/tr[3]/td[2]
或者更灵活一点,先定位到某个已知元素,再用轴找它的邻居:
//span[text()='用户名']/following-sibling::input
为什么会这样?因为很多时候,页面上有文字标签,但输入框本身没有特征。用轴就能轻松搞定。
三、XPath常用函数:让定位更精准
XPath自带一些函数,能帮你做更精细的筛选。我常用的有这几个:
- text():匹配文本内容。比如
//button[text()='提交'] - contains():包含匹配。比如
//input[contains(@class, 'search')] - starts-with():开头匹配。比如
//div[starts-with(@id, 'result')] - not():取反。比如
//input[not(@disabled)] - normalize-space():去除前后空格。比如
//span[normalize-space()='确定']
来看个综合例子。假设页面上有个搜索框,它的id是动态生成的,但placeholder是固定的:
//input[contains(@placeholder, '搜索')]
这样不管id怎么变,定位都不会失效。我在项目中遇到过类似场景,当时用contains()一次搞定,同事还在那纠结怎么处理动态id呢。
四、CSS Selector定位:更快更简洁
CSS Selector是Selenium官方推荐的定位方式。为什么?因为它比XPath快,而且写法更简洁。说白了,XPath能做的,CSS Selector大部分也能做,但CSS Selector写起来更清爽。
1. ID选择器
用#号表示:
#username // 等价于 //*[@id='username']
2. 类选择器
用点号表示:
.login-btn // 等价于 //*[@class='login-btn']
3. 属性选择器
用方括号表示:
input[name='email'] // 属性精确匹配
input[placeholder*='邮箱'] // 包含匹配(类似contains)
input[type^='sub'] // 开头匹配(类似starts-with)
input[class$='btn'] // 结尾匹配
4. 层级选择器
CSS Selector的层级表达比XPath更直观:
div.login-box > input // 直接子元素(>号)
div.login-box input // 后代元素(空格)
label + input // 相邻兄弟元素(+号)
h2 ~ p // 通用兄弟元素(~号)
举个例子。你想定位登录框里的提交按钮,用CSS Selector可以这样写:
div.login-form > button[type='submit']
用XPath写就是:
//div[@class='login-form']/button[@type='submit']
你对比一下,哪个更清爽?我个人习惯是,能用CSS Selector绝不用XPath。除非遇到文本匹配、轴操作这些CSS搞不定的场景,我才会切回XPath。
五、实战对比:CSS vs XPath
咱们来看几个实际场景,对比一下两种定位方式的写法:
| 场景 | CSS Selector | XPath |
|---|---|---|
| 按ID定位 | #username | //*[@id='username'] |
| 按class定位 | .btn-primary | //*[@class='btn-primary'] |
| 按属性定位 | input[type='text'] | //input[@type='text'] |
| 按文本定位 | 不支持 | //button[text()='提交'] |
| 按部分属性匹配 | input[class*='btn'] | //input[contains(@class, 'btn')] |
| 找父元素 | 不支持 | //input/parent::div |
看到没?CSS Selector在大部分场景下更简洁,但遇到文本匹配和轴操作时,就得靠XPath了。嗯,这里要注意——不要死磕一种方式,两种都掌握,根据场景灵活切换才是正道。
好了,这一章的内容就到这。下一章我们会讲等待机制——说实话,这是自动化测试里最容易踩坑的地方。到时候我会分享一些我踩过的坑,保证让你少走弯路。