3、元素定位基础:CSS选择器、XPath定位、文本定位、相对定位器、定位策略选择

好,咱们进入第三讲。元素定位,说白了就是让浏览器知道「你要操作哪个东西」。我刚开始做自动化的时候,觉得这玩意儿没啥技术含量,不就是找个按钮嘛。结果呢?脚本跑着跑着就报错,一查,元素没找到。嗯,从那以后我再也不敢小看定位了。

这一章,我会把五种主流定位方式掰开揉碎讲清楚。你跟着走一遍,以后遇到任何页面,心里都有底。

3.1 CSS选择器:又快又稳的老大哥

CSS选择器,我个人最常用。为什么?因为它快。浏览器解析CSS选择器是原生支持的,性能上比XPath好不少。我在做双十一大促的自动化脚本时,页面元素几百个,用CSS选择器定位,响应速度明显快一截。

常用的几种写法,我列出来:

选择器类型 示例 说明
ID选择器 #login-btn 定位id="login-btn"的元素
类选择器 .product-card 定位class包含"product-card"的元素
属性选择器 [data-type="submit"] 定位data-type属性值为submit的元素
后代选择器 div .price 定位div内部class为price的元素
子元素选择器 ul > li 定位ul的直接子元素li
相邻兄弟选择器 h2 + p 定位紧跟在h2后面的p元素
我的小技巧: 在Chrome开发者工具的Elements面板里,选中元素后右键 → Copy → Copy selector,可以直接拿到一个CSS选择器。不过它生成的路径往往又长又臭,我一般只拿来参考,自己再精简。

举个例子,你要定位一个「加入购物车」按钮,它的HTML可能是这样:

<button class="btn btn-primary" data-action="add-to-cart">加入购物车</button>

用CSS选择器可以这么写:

button.btn-primary[data-action="add-to-cart"]

你看,组合使用类选择器和属性选择器,定位非常精准。我在项目中遇到过一个问题:页面上有多个「加入购物车」按钮,但只有一个是当前商品对应的。这时候加上属性选择器,就能精确锁定目标。

3.2 XPath定位:功能最全的瑞士军刀

XPath,功能是真的强。它能往上找父元素,能根据文本内容定位,还能做复杂的逻辑判断。CSS选择器做不到的,它基本都能做。但代价是什么?性能稍差,语法也啰嗦一些。

XPath有两种写法,我简单说一下:

  • 绝对路径:从根节点开始写,比如 /html/body/div[2]/div[1]/button。这种写法很脆弱,页面结构稍微一变就挂了。我从来不用。
  • 相对路径:用 // 开头,比如 //button[@class='btn-primary']。灵活,好维护,推荐。

常用的XPath表达式:

表达式 示例 说明
根据属性定位 //input[@id='username'] 定位id为username的input
根据文本定位 //button[text()='提交订单'] 定位文本为"提交订单"的按钮
包含文本 //span[contains(text(),'优惠')] 定位文本包含"优惠"的span
多条件组合 //div[@class='item' and @data-id='123'] 同时满足两个条件
轴定位 //label[@for='email']/following-sibling::input 定位label后面的兄弟元素input
注意: XPath的索引是从1开始的,不是0。我第一次写XPath时用了 [0],结果死活定位不到。查了半天文档才发现这个坑。你记住,XPath里 [1] 才是第一个元素。

举个例子,你想定位一个「确认删除」的弹窗按钮,但页面上可能有多个弹窗。这时候可以结合文本定位:

//div[contains(@class,'modal')]//button[text()='确认删除']

先找到弹窗容器,再在里面找按钮。这样即使页面其他地方有同名按钮,也不会误定位。

3.3 文本定位:最直观的方式

文本定位,说白了就是「看到什么就定位什么」。你想想看,用户操作页面时,眼睛看的是文字,手点的是按钮。那自动化脚本为什么不能直接用文字来定位呢?

在Selenium 4里,文本定位被正式支持了。有两种:

  • By.LINK_TEXT:精确匹配链接文本
  • By.PARTIAL_LINK_TEXT:模糊匹配链接文本

不过要注意,这两个只能用于 <a> 标签。如果你想定位普通元素的文本,还是得用XPath的 text() 函数。

举个例子:

# 精确匹配
driver.find_element(By.LINK_TEXT, "查看订单详情")

# 模糊匹配
driver.find_element(By.PARTIAL_LINK_TEXT, "订单")

我在项目中遇到过一个问题:一个「立即购买」按钮,它的文本是动态生成的,有时候是「立即购买」,有时候是「马上抢购」。这时候用精确匹配就不行了,得用 contains(text(),'购买') 或者 PARTIAL_LINK_TEXT

3.4 相对定位器:Selenium 4的新玩具

相对定位器,是Selenium 4引入的新功能。它允许你根据元素之间的相对位置来定位。比如「在某个元素左边的按钮」、「在某个元素下方的输入框」。

常用的相对定位方法:

方法 说明
above() 定位目标元素上方的元素
below() 定位目标元素下方的元素
toLeftOf() 定位目标元素左侧的元素
toRightOf() 定位目标元素右侧的元素
near() 定位目标元素附近(50像素内)的元素

举个例子,你有一个商品列表,每个商品下面都有一个「加入购物车」按钮。你想定位第三个商品下面的按钮,可以这样:

from selenium.webdriver.support.relative_locator import locate_with

# 先找到第三个商品
third_product = driver.find_element(By.XPATH, "//div[@class='product'][3]")

# 再定位它下方的按钮
add_cart_btn = driver.find_element(
    locate_with(By.TAG_NAME, "button").below(third_product)
)
我的建议: 相对定位器适合处理那些结构复杂、但相对位置固定的页面。比如表格里的操作按钮、列表里的编辑链接。不过别滥用,能用CSS或XPath搞定的,优先用它们。

3.5 定位策略选择:什么时候用什么

好,五种定位方式都讲完了。你可能会问:那我到底该用哪个?

我个人的选择优先级是这样的:

  1. 优先用ID:如果元素有唯一的ID,直接用 By.ID。最快,最稳。
  2. 其次用CSS选择器:ID没有,就用CSS。性能好,语法简洁。
  3. 文本定位做补充:链接、按钮这些,用文本定位最直观。
  4. XPath兜底:前面都不行,或者需要复杂逻辑(比如根据文本、属性、层级组合定位),上XPath。
  5. 相对定位器处理特殊场景:比如「某个元素旁边的按钮」,用相对定位器最合适。

我总结了一个简单的决策表:

场景 推荐定位方式 示例
元素有唯一ID By.ID #submit-btn
元素有唯一class或属性 CSS选择器 .btn-primary
需要根据文本定位 XPath text() 或 LINK_TEXT //button[text()='提交']
需要根据相对位置定位 相对定位器 below(element)
需要复杂逻辑组合 XPath //div[@class='item' and .//span='价格']
页面结构经常变化 文本定位或相对定位器 减少对DOM结构的依赖

核心原则: 定位器越简单越好,越稳定越好。不要为了炫技写复杂的XPath,能用一行CSS搞定的,别写五行的XPath。你想想看,脚本是要长期维护的,简单的东西出错的概率更低。

嗯,这一章的内容就到这里。五种定位方式,每一种都有它的用武之地。你平时写脚本的时候,多试试不同的方式,慢慢就会形成自己的判断。下一章,咱们聊聊「等待机制」,这可是解决脚本不稳定问题的关键。