上一节课咱们搞定了 CSS Selector,是不是已经能应对大部分复杂场景了?但如果遇到 "需要根据文本内容定位"、"元素层级特别复杂" 的情况,CSS Selector 就有点 "力不从心" 啦~ 这时候,XPath 这个 "全能王者" 就要登场!它不仅兼容 CSS 的大部分功能,还能直接定位文本、处理更灵活的层级关系,这节课咱们就彻底吃透 XPath,让你在自动化测试中 "无往不利"!
XPath 是什么?------ 比CSS更灵活的"定位神器"
XPath(XML Path Language)原本是用于解析XML文档的语言,因为它能遍历文档中任意节点,被完美应用到自动化测试的元素定位中。和CSS Selector相比,它的核心优势的是:
- 支持文本定位(直接根据元素显示的文字找,CSS不支持!);
- 支持反向定位(通过子元素找父元素,CSS做不到!);
- 层级关系更灵活,支持复杂的节点遍历;
- 兼容所有浏览器和移动端测试框架(Appium也能用!)。
简单说:CSS 是 "精准高效",XPath 是 "全能灵活",两者结合,自动化定位就没有解决不了的问题!
XPath 核心语法实战 ------ 从基础到高级,步步为营
XPath基础语法
以下表中是XPath选取节点的基础语法表达式:
|--------------|-----------------|
| 表达式 | 描述 |
| nodename | 选取此名称的所有子节点 |
| / | 从该节点的子元素选取 |
| // | 从该节点的子孙元素选取 |
| . | 选取当前节点 |
| .. | 选取当前节点的父节点 |
| @ | 选取属性 |
该部分的讲解,为了方便大家更好的理解XPath的基础语法,我们自定义一个HTML的结构进行案例讲解。
html
<!DOCTYPE html>
<html>
<head>
<title>XPath 学习示例</title>
</head>
<body>
<div id="container">
<h1 class="title">欢迎学习 XPath</h1>
<p>这是第一段介绍。</p>
<div class="content">
<p>这是第二段介绍,位于一个嵌套的 div 中。</p>
<a href="https://www.example.com" target="_blank">访问示例网站</a>
</div>
</div>
<p>这是最后一段介绍,在 container div 的外面。</p>
</body>
</html>
1、选取节点(nodename)
语法: nodename
作用: 直接写节点名称,选取所有该名称的子节点(相对当前上下文)。
示例: 在Html中,选取所有< p >节点
Xpath表达式://p (结合 // 从全局查找)
结果:选取到 3 个 <p> 节点,分别是:
- <p>这是第一段介绍。</p>
- <p>这是第二段介绍,位于一个嵌套的 div 中。</p>
- <p>这是最后一段介绍,在 container div 的外面。</p>
2. 绝对路径(/)
语法: /根节点/子节点/...
作用: 从文档根节点开始,逐级向下选取直接子节点(类似文件系统的绝对路径)。
示例: 从根节点出发,精确查找嵌套的 <a> 标签
XPath 表达式:/html/body/div/div/a
解析:
- /html: 从文档的根节点 <html> 开始。
- /body: 选取 <html> 的直接子节点 <body>。
- /div: 选取 <body> 的直接子节点 <div>(即 div#container)。
- /div: 选取上一个 div 的直接子节点 <div>(即 div.content)。
- /a: 选取上一个 div 的直接子节点 <a>。
结果:精确地选取到 1 个节点,即 <a href="https://www.example.com" ...>。
3. 相对路径(//)
语法: //节点 或 上下文//节点
作用: 不考虑节点在文档中的位置(忽略层级),从当前上下文(或全局)查找所有匹配的节点(包括子孙节点)。
示例: 在Html中,选取所有< p >节点
XPath表达式://p
结果:3 个 <p> 都被选中,分别是:
- <p>这是第一段介绍。</p>
- <p>这是第二段介绍,位于一个嵌套的 div 中。</p>
- <p>这是最后一段介绍,在 container div 的外面。</p>
4. 当前节点(.)
语法: .
作用: 表示 "当前节点",常用于限定查找范围(避免全局查找的歧义)。
示例: 假设我们已经定位到id="container"的 div 节点,现在要在这个 div 内部找a。
XPath 表达式:.//a
解析:. 代表当前的 div 节点,//a 表示在当前节点的所有后代中查找 <a> 标签。
结果:选取到 1 个节点,即 <a href="https://www.example.com" ...>。
5. 父节点(..)
语法: ..
作用: 表示 "当前节点的父节点",用于向上回溯层级。
示例: 查找 <a> 标签的父节点
XPath表达式://a/..
解析://a 找到文档中的 <a> 标签,/.. 表示选取该 <a> 节点的父节点。
结果:选取到 1 个节点,即 <div class="content">。
6. 选取属性(@)
语法: @属性名
作用: @ 符号用于选取节点的属性。
示例: 查找 id 属性值为 container 的 div 节点
XPath 表达式: //div[@id="container"]
解析: 这是最常用的场景之一。[] 中的内容是条件表达式。@id="container" 表示节点的 id 属性值必须严格等于 "container"。
结果: 精确地选取到 1 个节点,即 <div id="container">。
XPath高级语法
我们可以将高级语法理解为 XPath 的 "查询条件" 和 "定位策略" 的扩展。
- 谓语 (Predicate) []: 这是最核心的高级特性,用来过滤节点。所有复杂的条件都写在方括号 [] 内。
- 运算符 (Operators): 在谓语内部使用,用于比较、计算和逻辑判断(如 =, !=, >, <, and, or, +, - 等)。
- 轴 (Axes): 定义了当前节点与目标节点之间的 "树状关系"(如 child, parent, sibling, ancestor 等),让你能以更精细的方式定位节点。
- 函数(Function):提供文本处理、数值计算、节点位置等能力(如:text(), position(), contains(), starts-with()等)。
谓语、运算符、轴、函数的完整定义可参考官方文档,这里我们将通过任务驱动的方式,结合下方的 HTML 文档实例,重点学习这些特性的实际用法。
html
<!DOCTYPE html>
<html>
<head>
<title>XPath 学习示例</title>
</head>
<body>
<div id="container">
<h1 class="title">欢迎学习 XPath</h1>
<p>这是第一段介绍。</p>
<div class="content">
<p>这是第二段介绍,位于一个嵌套的 div 中。</p>
<a href="https://www.example.com" target="_blank">访问示例网站</a>
<p>这是第三段介绍,也在 content div 中。</p>
</div>
<p>这是第四段介绍。</p>
</div>
<p>这是最后一段介绍,在 container div 的外面。</p>
<ul>
<li>列表项 1</li>
<li class="highlight">列表项 2 (高亮)</li>
<li>列表项 3</li>
</ul>
</body>
</html>
任务一:精准定位 - 使用 "属性" 和 "文本" 作为条件
目标1: 找到 class 为 highlight 的列表项 <li>。
XPath 表达式: //li[@class="highlight"]
解析:
- //li: 找到文档中所有的 <li> 节点。
-
...\]: 这是一个谓语,对前面找到的 \
- 节点进行过滤。
- @class: 使用 @ 选取 class 属性。
- =: 这是一个比较运算符,表示 "等于"。
- "highlight": 字符串字面量,作为比较的目标值。
结果: 精确地选取到 <li class="highlight">列表项 2 (高亮)</li>。
目标2:找到文本内容为 "这是第四段介绍。" 的 <p> 节点。
XPath 表达式: //p[text()="这是第四段介绍。"]
解析:
- text(): 这是一个函数,返回节点的文本内容。
- text()="这是第四段介绍。": 在谓语中,我们比较节点的文本内容是否等于指定字符串。
任务二:范围查找 - 使用 "逻辑运算" 和 "位置"
目标: 找到 div#container 下的第 2 个和第 3 个 <p> 节点。
XPath 表达式: //div[@id="container"]/p[position()=2 or position()=3]
解析:
- //div[@id="container"]/p: 首先定位到 div#container 的直接子节点 <p>。
-
...\]: 谓语,对这组 \
节点进行过滤。
- position(): 一个函数,返回节点在其兄弟节点中的位置(从 1 开始)。
- =: 比较运算符。
- or: 逻辑运算符,表示 "或"。
结果: 选取到 "这是第二段介绍..." 和 "这是第三段介绍..." 这两个 <p> 节点。
更简洁的写法: //div[@id="container"]/p[position()>=2 and position()<=3],这里使用了 >=, <= 和 and。
任务三:复杂关系定位 - 使用 "轴" 和 "节点关系"
目标1: 找到 <li class="highlight"> 节点的前一个兄弟节点。
XPath 表达式: //li[@class="highlight"]/preceding-sibling::li[1]
解析:
- //li[@class="highlight"]: 首先,定位到我们的 "基准节点"。
- /preceding-sibling::: 这是一个轴 (Axis)。
- preceding-sibling 轴定义了所有在基准节点之前、且与基准节点拥有同一个父节点的节点。
- ::: 轴分隔符,用于连接轴名称和节点测试。
- li: 节点测试。在 preceding-sibling 轴找到的所有节点中,我们只想要 <li> 节点。
-
1\]: 这是一个简化的谓语,表示取第一个符合条件的节点。
目标2: 找到 <a> 标签(链接)的父节点的父节点(即 div#container)。
XPath 表达式: //a/ancestor::div[@id="container"]
解析:
- //a: 找到所有 <a> 标签。
- /ancestor::: 这是一个轴,表示查找基准节点的所有祖先节点(父、祖父、曾祖父等)。
- div[@id="container"]: 在所有祖先节点中,我们只想要 id 为 container 的 <div> 节点。
结果: 选取到 <div id="container">。
对比: //a/parent::*/parent::div 也能达到同样效果,但 ancestor 轴更直接、更优雅。
任务四:动态内容提取 - 使用 "计算" 和 "模糊匹配"
目标1: 假设有一个网页,其列表项的 id 是动态生成的,如 item-1, item-2, item-3...,我们想找到 id 以 item- 开头的所有 <li>。
XPath 表达式: //li[starts-with(@id, "item-")]
解析:
- starts-with(): 这是一个字符串函数,用于模糊匹配。
- starts-with(@id, "item-"): 条件为 "id 属性的值是否以 item- 开头"。
结果: 所有 id 如 item-1, item-2 等的 <li> 都会被选中。
目标2: 找到 div#container 中,文本内容包含 "示例" 二字的 <a> 标签。
XPath 表达式: //div[@id="container"]//a[contains(text(), "示例")]
解析:
- contains(): 另一个常用的字符串函数,表示 "包含"。
- contains(text(), "示例"): 条件为 "节点的文本内容是否包含'示例'这个子字符串"。
结果: 选取到 <a href="https://www.example.com" ...>访问示例网站</a>。
案例实战
还是以登录百度为例,使用本节课讲解的内容,定位到"登录"按钮并点击打开登录对话框,然后输出用户名和密码, 代码改造如下:
python
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
def test_baidu():
# 初始化浏览器驱动(chrome浏览器)
driver = webdriver.Chrome()
# 打开百度
driver.get("https://www.baidu.com/")
# 定位到登录按钮,并点击
driver.find_element(By.XPATH,"//a[@id='s-top-loginbtn']").click()
sleep(3)
# 定位到手机号码输入框,并输入
driver.find_element(By.XPATH,"//input[@name='userName']").send_keys("15111111111")
# 找到密码输入框,并输入密码
driver.find_element(By.XPATH,"//input[@placeholder='密码']").send_keys("79&abe$")
# 等待3秒
sleep(3)
# 关闭浏览器
driver.quit()
if __name__ == "__main__":
test_baidu()
小结
XPath 是自动化测试的 "全能定位工具",尤其是高级定位方式,完美弥补了 CSS Selector 的不足!核心要掌握 "文本匹配" 和 "层级遍历",再配合属性模糊匹配,无论是动态元素、复杂嵌套,还是无属性元素,都能轻松搞定~ 建议大家把 XPath 和 CSS Selector 结合使用:简单场景用 CSS(高效简洁),复杂场景用 XPath(灵活全能)。
到这里,复杂定位的两大神器就全部讲解完毕啦!从基础定位到 CSS、XPath,咱们已经掌握了自动化测试元素定位的核心技能~ 接下来可以结合实际项目练手,比如搭建一个简单的测试脚本,用不同的定位方式定位页面元素,对比它们的优劣。如果在练习中遇到具体问题,或者想了解定位工具(如 Chrome 开发者工具)的使用技巧,欢迎随时告诉我哦!