做Web自动化测试或者网页数据抓取的时候,XPath绝对是个绕不开的利器。它能通过组合各种条件,精准命中你需要的那个元素。不过,当定位条件变得复杂——比如要同时满足几个属性,或者从一堆可能性里挑一个——怎么把条件组织得漂亮、高效,就是个技术活了。今天咱们就聊聊,在Python里怎么用好XPath的and和or这两个逻辑运算符,搞定那些“既要…又要…”或者“要么…要么…”的定位需求。

一、XPath逻辑运算符基础
XPath里主要有三个逻辑运算符:
- and:逻辑与,要求所有条件同时成立,缺一不可。
- or:逻辑或,满足其中任何一个条件就行。
- not:逻辑非,对某个条件取反,排除掉不想匹配的。
说到底,这些运算符需要在方括号[]构成的谓词里使用,而且两侧必须是能算出来真假的布尔表达式,比如属性存不存在、某个函数返回的结果对不对。
二、and运算符实战:精准匹配多属性
场景1:同时匹配多个属性
比如页面上有个提交按钮,它既有class="btn",又有type="submit",两个属性都满足才是我们要的那个。用and一把锁住:
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("https://example.com/login")
# 使用and组合条件
submit_btn = driver.find_element("xpath", '//button[@class="btn" and @type="submit"]')
submit_btn.click()
场景2:属性与文本内容组合
有时候还得把属性和文本内容结合起来。比如一个链接,它class属性包含“active”,并且显示的文本正好是“登录”:
login_link = driver.find_element("xpath", '//a[contains(@class,"active") and text()="登录"]')
这样一来,即使页面上有多个active的链接,也能精确锁定那个叫“登录”的。
三、or运算符实战:匹配多种可能性
场景1:多属性值匹配
表单里有些输入框,type可能是“text”,也可能是“email”。不管具体是哪个,我们要把这两种都抓出来。这时候or就派上用场了:
input_field = driver.find_element("xpath", '//input[@type="text" or @type="email"]')
场景2:多标签类型匹配
更高级一点的玩法,是跨标签类型定位。比如按钮可能是,也可能是个标签,但只要里面文字包含“保存”,就算符合条件的元素。用or再加个|左右开弓:
sa ve_element = driver.find_element("xpath", '//button[text()="保存"] | //a[contains(text(),"保存")]')
四、复杂条件组合:括号明确优先级
当and和or混着用时,逻辑优先级就容易混淆。比如“我要一个元素,它有id属性,并且它的文本包含‘搜索’或者它的placeholder包含‘关键词’”。如果写在一起而不加括号,浏览器可不一定按你的想法理解。
案例:定位百度搜索框
driver.get("https://www.baidu.com")
# 错误写法(这行代码的语义很容易让你看走眼):
# //input[@id and contains(text(),'搜索') or contains(@placeholder,'关键词')]
# 它实际等价于: (@id and contains(text(),'搜索')) or contains(@placeholder,'关键词')
# 而不是你想要的 @id and (contains(text(),'搜索') or contains(@placeholder,'关键词'))
# 正确写法,用括号把“或”的条件包起来,明确逻辑分组:
search_box = driver.find_element("xpath", '//input[@id and (contains(text(),"搜索") or contains(@placeholder,"关键词"))]')
更实用的组合示例
要找一个元素:它得有id属性,同时它的class等于“primary”,或者type等于“submit”。只要括号加的准,逻辑就稳:
element = driver.find_element("xpath", '//*[@id and (@class="primary" or @type="submit")]')
五、进阶技巧:结合函数使用
光会用and和or还不够,加上XPath内置的字符串函数,定位能力才算真正起飞。
1. 使用contains()实现模糊匹配
需要匹配class以“btn-”开头,同时id以“user_”开头的元素?contains()和starts-with()双管齐下:
# 匹配class包含"btn-"且id以"user_"开头的元素
elements = driver.find_elements("xpath", '//*[contains(@class,"btn-") and starts-with(@id,"user_")]')
2. 使用not()排除特定条件
有时候要排除掉一些干扰项。比如只想定位所有可见的输入框,排除掉那些被disabled的,还有type="hidden"的隐藏域:
# 定位所有可见的input元素(排除disabled和hidden的)
inputs = driver.find_elements("xpath", '//input[not(@disabled) and @type!="hidden"]')
六、性能优化建议
表达式写得再花哨,也得讲究效率。有几个原则值得记住:
- 优先使用唯一属性:能用
id、name这种唯一标识符就绝不含糊,别绕弯子。 - 减少层级嵌套:避免写那种从
/html/body/div[3]/...开始的长路径,又臭又长还容易因为页面结构调整而失效。 - 使用相对路径:以
//开头从文档根搜索,灵活得多。 - 限制结果范围:能用
(//div)[1]指定索引就别遍历所有元素,少一些开销。
七、完整案例:12306车票查询
最后看一个实战例子。在12306的车票查询页面上,日期选择器可能有多处,但我们要找的是跟出发站或到达站相关的日期输入框。这个场景把属性、文本、以及父级容器判断都揉在一起了:
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://kyfw.12306.cn/otn/leftTicket/init")
# 定位出发站为"北京"且到达站为"上海"的日期选择器
date_input = driver.find_element(
"xpath",
'//input[@id="date_range" and '
'((@placeholder="出发日期" and ancestor::div[contains(@class,"from-station")]) or '
'(@placeholder="到达日期" and ancestor::div[contains(@class,"to-station")]))]'
)
date_input.click()
总结
XPath的and/or运算符通过布尔逻辑组合条件,能解决不少棘手的定位问题:
- 多条件精确匹配
- 处理动态变化的属性值
- 解析结构复杂的页面
- 跨不同标签类型的灵活定位
掌握这些组合拳,绝大多数元素定位场景都能迎刃而解。平时写表达式的时候,不妨打开浏览器的开发者工具,用$x()函数实时验证一下,边写边测,效率会高很多。