做网页数据提取或者自动化测试时,经常需要精准地定位页面上的某个元素,这时候xpath就是最顺手的工具之一。它本质上是一种路径语言,通过描述元素在文档中的位置和特征,帮我们快速找到想要的内容。很多刚接触的朋友觉得xpath语法复杂,其实常用的表达式并不多,掌握十来个核心写法就能应付绝大多数场景。

xpath的基本路径表达式怎么写
xpath最基础的写法就是像写文件路径一样,一层一层往下找。比如 /html/body/div 表示从根节点开始,找到html下的body,再找到body直接子级中的div。这里要注意,单个斜杠表示只找直接子节点,而双斜杠 // 表示在所有后代中查找,不管隔了多少层。例如 //div 会找到页面中所有的div元素,这是使用频率最高的写法之一。
除了斜杠,还有一个特殊符号 @ 用来表示属性。比如 //a[@href] 表示选取所有带有href属性的a标签,而 //a[@href='https://www.baidu.com'] 则进一步要求href属性的值必须完全等于指定网址。属性匹配在实际开发中用处极大,因为网页元素的class、id等属性往往就是最好的定位线索。
如果想获取标签里的文字内容,可以在表达式末尾加上 /text()。比如 //h1/text() 就能拿到所有h1标签的文字。类似的还有 @属性名 直接取属性值,比如 //img/@src 可以批量获取所有图片的地址,在写爬虫下载图片时非常好用。
谓语筛选与常用函数有哪些
谓语就是写在方括号里的筛选条件,用来从一批节点中挑出符合要求的。最常见的几种用法包括按位置筛选和按内容筛选。按位置筛选时,//li[1] 表示选取第一个li元素,注意xpath的位置是从1开始数的,不是从0开始,这一点和编程语言的习惯不同,初学者很容易在这里犯错。
按内容筛选则要借助函数。contains() 是最常用的函数之一,写法如 //div[contains(@class,'content')],意思是选取class属性中包含content这个词的div。之所以要用contains,是因为很多网页的class值是多个词拼接的,用等号精确匹配经常匹配不到。类似的还有 starts-with(),用于匹配以某字符串开头的属性值。
还有几个实用函数值得记住。last() 表示最后一个,比如 //ul/li[last()] 选取列表的最后一项;position() 表示当前位置,可以配合比较符号使用,如 //li[position()<3] 选取前两个元素;not() 表示取反,//div[not(@id)] 可以找出所有没有id属性的div。这些函数组合起来,几乎能应对所有复杂的定位需求。
轴与逻辑运算在什么场景下使用
当目标元素本身没有明显特征,但它周围的元素有特征时,就需要用到xpath的轴概念。常见的有 parent:: 表示父节点,following-sibling:: 表示后面的兄弟节点,preceding-sibling:: 表示前面的兄弟节点。比如 //span[text()='价格']/following-sibling::em 就是先找到写着价格的span,再找它后面的em标签,这种写法在提取表格或列表数据时特别常见。
祖先和后代方向的轴也很实用,ancestor:: 用来向上找祖先节点,descendant:: 用来向下找所有后代。不过日常使用中,双斜杠基本可以替代descendant,所以更常用的是ancestor,比如需要从深层元素跳回某个父级容器再横向查找其他内容时。
逻辑运算方面,可以用 and 和 or 组合多个条件,也可以用竖线 | 把两个表达式合并,表示取两个结果的并集。比如 //div[@id='main'] | //div[@class='side'] 会同时选中id为main的div和class为side的div。这个特性在需要一次性提取页面多个区域数据时能省不少代码。
初学者常见疑问与避坑建议
第一个高频问题是:明明页面里有这个元素,为什么xpath匹配不到?最常见的原因有三个,一是元素在iframe里,需要先切换到iframe再执行查找;二是页面内容是JavaScript动态加载的,抓取时机太早导致还没渲染出来;三是属性值里含有看不见的空格或特殊字符,这时用contains代替精确等号往往就能解决。
第二个常见疑问是:匹配到了多个结果,怎么拿到具体某一个?xpath表达式返回的通常是节点列表,需要用下标取出具体项,比如 //div[@class='item'][2] 取第二个。还有一种情况是想同时拿到文字和属性,可以在一个大的括号表达式里用竖线合并,比如 //a/text() | //a/@href。
关于如何写出稳定不易失效的xpath,实践中有几条经验值得参考。优先使用语义明确的属性如id、name、data开头的自定义属性,少用class,因为class经常因为样式调整而变化;路径层级尽量短,避免从html开始写完整路径,那样页面结构稍一变动就失效;多用文本内容定位,比如 //button[contains(text(),'提交')],按钮文字一般不会随便改,比纯结构路径靠谱得多。
| 表达式 | 含义 | 典型用途 |
|---|---|---|
| //div | 选取所有div元素 | 快速圈定标签范围 |
| //a[@href='xx'] | href等于xx的链接 | 精确定位 |
| //div[contains(@class,'c')] | class包含c的div | 应对复合class值 |
| //li[last()] | 最后一个li | 取列表末尾数据 |
| //span/../div | span的父节点下的div | 借助相邻元素定位 |
| //a/text() | 获取链接文字 | 提取文本内容 |
总的来说,xpath入门并不难,先把单斜杠与双斜杠、属性匹配、text()取文本、谓语位置筛选这几个基础练熟,再逐步掌握contains等函数和兄弟轴的用法,配合浏览器开发者工具反复调试验证,很快就能写出又准又稳的表达式。遇到匹配不到的情况,多从iframe、动态加载和属性值空格这三个方向排查,基本能解决九成以上的问题。