一. 什么是Xpath?

XPath 是 XML 的查询语言,本节介绍该语言的语法。

XPath 使用路径表达式来选取 XML 文档中的节点或节点集。节点是通过沿着路径 (path) 或者步 (steps) 来选取的。

二. 快速使用

1. 前期准备

doc = '''
<html>
 <head>
  <base href='http://example.com/' />
  <title>Example website</title>
 </head>
 <body>
  <div id='images'>
   <a href='image1.html' aa='bb'>Name: My image 1 <br /><img src='image1_thumb.jpg' /></a>
   <a href='image2.html'>Name: My image 2 <br /><img src='image2_thumb.jpg' /></a>
   <a href='image3.html'>Name: My image 3 <br /><img src='image3_thumb.jpg' /></a>
   <a href='image4.html'>Name: My image 4 <br /><img src='image4_thumb.jpg' /></a>
   <a href='image5.html' class='li li-item' name='items'>Name: My image 5 <br /><img src='image5_thumb.jpg' /></a>
   <a href='image6.html' name='items'><span><h5>test</h5></span>Name: My image 6 <br /><img src='image6_thumb.jpg' /></a>
  </div>
 </body>
</html>
'''

2. 调用使用

from lxml import etree

html = etree.HTML(doc)
# html=etree.parse('search.html',etree.HTMLParser())

3. 所有节点

a = html.xpath('//*')
print(a)
'''
[<Element html at 0x1739ea9d108>, <Element head at 0x1739ed18548>, <Element base at 0x1739ed184c8>, <Element title at 0x1739ed18588>, <Element body at 0x1739ed185c8>, <Element div at 0x1739ed18648>, <Element a at 0x1739ed18688>, <Element br at 0x1739ed186c8>, <Element img at 0x1739ed18708>, <Element a at 0x1739ed18608>, <Element br at 0x1739ed18748>, <Element img at 0x1739ed18788>, <Element a at 0x1739ed187c8>, <Element br at 0x1739ed18808>, <Element img at 0x1739ed18848>, <Element a at 0x1739ed18888>, <Element br at 0x1739ed188c8>, <Element img at 0x1739ed18908>, <Element a at 0x1739ed18948>, <Element br at 0x1739ed18988>, <Element img at 0x1739ed189c8>, <Element a at 0x1739ed18a08>, <Element span at 0x1739ed18a48>, <Element h5 at 0x1739ed18a88>, <Element br at 0x1739ed18ac8>, <Element img at 0x1739ed18b08>]
'''

4. 指定节点(结果为列表)

a = html.xpath('//head')
print(a)  # [<Element head at 0x29d0dff8548>]

5. 子节点,子孙节点

a = html.xpath('//div/a')
print(a)
'''
[<Element a at 0x20093fe85c8>, <Element a at 0x20093fe8548>, <Element a at 0x20093fe8608>, <Element a at 0x20093fe8648>, <Element a at 0x20093fe8688>, <Element a at 0x20093fe8708>]
'''

a = html.xpath('//body/a')  # 无数据
print(a)  # []

a = html.xpath('//body//a')
print(a)
'''
[<Element a at 0x20093fe86c8>, <Element a at 0x20093fe85c8>, <Element a at 0x20093fe8548>, <Element a at 0x20093fe8608>, <Element a at 0x20093fe8648>, <Element a at 0x20093fe8708>]
'''

5. 父节点

a = html.xpath('//body//a[@href="image1.html"]/..')
print(a)  # [<Element div at 0x2afcd6e8588>]

a = html.xpath('//body//a[1]/..')
print(a)  # [<Element div at 0x2afcd6e8588>]

# 也可以这样
a = html.xpath('//body//a[1]/parent::*')
print(a)  # [<Element div at 0x2afcd6e8588>]

6. 属性匹配

a = html.xpath('//body//a[@href="image1.html"]')
print(a)  # [<Element a at 0x18ae52784c8>]

7. 文本获取: /text() 取当前标签的文本

a = html.xpath('//body//a[@href="image1.html"]/text()')
print(a)  # ['Name: My image 1 ']

a = html.xpath('//body//a/text()')
print(a)
'''
['Name: My image 1 ', 'Name: My image 2 ', 'Name: My image 3 ', 'Name: My image 4 ', 'Name: My image 5 ', 'Name: My image 6 ']
'''

8. 属性获取: @href 取当前标签的属性

a = html.xpath('//body//a/@href')
print(a)
'''
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html', 'image6.html']
'''

# 注意从1 开始取(不是从0)
a = html.xpath('//body//a[1]/@href')
print(a)  # ['image1.html']

9. 属性多值匹配

# a 标签有多个class类,直接匹配就不可以了,需要用contains
a = html.xpath('//body//a[@class="li"]')
print(a)  # []

a = html.xpath('//body//a[contains(@class,"li")]')
print(a)  # [<Element a at 0x2de3717c548>]

a = html.xpath('//body//a[contains(@class,"li")]/text()')
print(a)  # ['Name: My image 5 ']

10. 多属性匹配

a = html.xpath('//body//a[contains(@class,"li") or @name="items"]')
print(a)  # [<Element a at 0x26440c6c548>, <Element a at 0x26440c6c4c8>]

a = html.xpath('//body//a[contains(@class,"li") and @name="items"]/text()')
print(a)  # ['Name: My image 5 ']

a = html.xpath('//body//a[contains(@class,"li")]/text()')
print(a)  # ['Name: My image 5 ']

11. 按序选择

a = html.xpath('//a[2]/text()')
print(a)  # ['Name: My image 2 ']

a = html.xpath('//a[2]/@href')
print(a)  # ['image2.html']

# 取最后一个
a = html.xpath('//a[last()]/@href')
print(a)  # ['image6.html']

# 位置小于3的
a = html.xpath('//a[position()<3]/@href')
print(a)  # ['image1.html', 'image2.html']

# 倒数第二个
a = html.xpath('//a[last()-2]/@href')
print(a)  # ['image4.html']

12. 节点轴选择

1) ancestor:祖先节点

# 使用了* 获取所有祖先节点
a = html.xpath('//a/ancestor::*')
print(a)  # [<Element html at 0x1ed8898d108>, <Element body at 0x1ed88c28608>, <Element div at 0x1ed88c28588>]

# 获取祖先节点中的div
a = html.xpath('//a/ancestor::div')
print(a)  # [<Element div at 0x1ed88c28588>]

# attribute:属性值
a = html.xpath('//a[1]/attribute::*')
print(a)  # ['image1.html', 'bb']

a = html.xpath('//a[1]/@aa')
print(a)  # ['bb']

2) child:直接子节点

a = html.xpath('//a[1]/child::*')
print(a)  # [<Element br at 0x1ec054e8588>, <Element img at 0x1ec054e8608>]

a = html.xpath('//a[1]/child::img/@src')
print(a)  # ['image1_thumb.jpg']

3) descendant:所有子孙节点

a = html.xpath('//a[6]/descendant::*')
print(a)  # [<Element span at 0x20b3d2ab588>, <Element h5 at 0x20b3d2ab5c8>, <Element br at 0x20b3d2ab608>, <Element img at 0x20b3d2ab648>]

a = html.xpath('//a[6]/descendant::h5/text()')
print(a)  # ['test']

4) following: 当前节点之后所有节点(兄弟节点和兄弟内部的节点)

a = html.xpath('//a[1]/following::*')
print(a)
'''
[<Element a at 0x20749da2548>, <Element br at 0x20749da24c8>, <Element img at 0x20749da2588>, <Element a at 0x20749da25c8>, <Element br at 0x20749da2608>, <Element img at 0x20749da2688>, <Element a at 0x20749da26c8>, <Element br at 0x20749da2708>, <Element img at 0x20749da2748>, <Element a at 0x20749da2648>, <Element br at 0x20749da2788>, <Element img at 0x20749da27c8>, <Element a at 0x20749da2808>, <Element span at 0x20749da2848>, <Element h5 at 0x20749da2888>, <Element br at 0x20749da28c8>, <Element img at 0x20749da2908>]
'''

a = html.xpath('//a[1]/following::*[1]/@href')
print(a)  # ['image2.html']

5) following-sibling:当前节点之后同级节点(只找兄弟)

a = html.xpath('//a[1]/following-sibling::*')
print(a)
'''
[<Element a at 0x20749da2548>, <Element a at 0x20749da2588>, <Element a at 0x20749da25c8>, <Element a at 0x20749da2608>, <Element a at 0x20749da2688>]
'''

a = html.xpath('//a[1]/following-sibling::a')
print(a)  
'''
[<Element a at 0x20749da2548>, <Element a at 0x20749da2588>, <Element a at 0x20749da25c8>, <Element a at 0x20749da2608>, <Element a at 0x20749da2688>]
'''

a = html.xpath('//a[1]/following-sibling::*[2]')
print(a)  # [<Element a at 0x20749da2588>]

a = html.xpath('//a[1]/following-sibling::*[2]/@href')
print(a)  # ['image3.html']

三. xpath语法参考表

1. 选取节点

XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。 下面列出了最有用的路径表达式:

表达式 描述
nodename 选取此节点的所有子节点。
/ 从根节点选取。
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
. 选取当前节点。
.. 选取当前节点的父节点。
@ 选取属性。

在下面的表格中,我们已列出了一些路径表达式以及表达式的结果:

路径表达式 结果
bookstore 选取 bookstore 元素的所有子节点。
/bookstore 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book 选取属于 bookstore 的子元素的所有 book 元素。
//book 选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
//@lang 选取名为 lang 的所有属性。

提示:如果 XPath 的开头是一个斜线(/)代表这是绝对路径。如果开头是两个斜线(//)表示文件中所有符合模式的元素都会被选出来,即使是处于树中不同的层级也会被选出来。

1. 谓语(Predicates)

谓语用来查找某个特定的节点或者包含某个指定的值的节点。

谓语被嵌在方括号中。

在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:

路径表达式 结果
/bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。
/bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。
/bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。
/bookstore/book[position()❤️] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
//title[@lang] 选取所有拥有名为 lang 的属性的 title 元素。
//title[@lang='eng'] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
/bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

3. 选取未知节点

XPath 通配符可用来选取未知的 XML 元素。

通配符 描述
* 匹配任何元素节点。
@* 匹配任何属性节点。
node() 匹配任何类型的节点。

在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式 结果
/bookstore/* 选取 bookstore 元素的所有子元素。
//* 选取文档中的所有元素。
//title[@*] 选取所有带有属性的 title 元素。

4. 选取若干路径

通过在路径表达式中使用"|"运算符,您可以选取若干个路径。

在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式 结果
//book/title | //book/price 选取 book 元素的所有 title 和 price 元素。
//title | //price 选取文档中的所有 title 和 price 元素。
/bookstore/book/title | //price 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。