1 XPath

1.1 功能

使用路径表达式来选择xml文档中的node或者nodes. 其中每一个节点是通过验证路径(path)或者
步(steps)来选取的, 这也是XPath的由来.

本章所有讨论例子都以下面为准:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="ISO-8859-1"?>

<bookstore>
<book>
<title lang="eng">Harry Potter</title>
<price>29.99</price>
</book>
<book>
<title lang="eng">Learning XML</title>
<price>39.95</price>
</book>
</bookstore>

1.2 选取节点

利用路径表达式来完成节点的选取, 重复一遍: 节点沿着path/step来选取的, 可选的路径表达式:

  • nodename: 选取此节点的所有子节点
  • /: 从根节点选取
  • //: 从当前节点的子节点中开始查找, 不指定起始位置
  • .: 当前节点
  • ..: 当前节点父节点
  • preceding::X: 获取当前节点之前的所有节点, 例如proceding::li[3], 获取上面第三个li
  • following-sibling::X: 获取所有统计节点
  • @: 选取属性

未知节点或者通配符节点

  • *: 匹配任何元素节点
  • @*: 匹配任何属性节点
  • node(): 匹配任意类型节点

使用|来选择多个路径

  • //book/title | //book/price: 选取 book 元素的所有 title 和 price 元素
  • //title | //price: 选取文档中的所有 title 和 price 元素
  • /bookstore/book/title | //price: 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素

1.3 谓语(Predicates)

用来查找某个特定的节点或者包含某个指定的值的节点, 格式如下: [谓语], 与路径表达式配合, 进一步
缩小选择范围.

1
2
3
4
5
6
7
8
9
A[1]/A.extract_first(): 选择第一个元素A, 例如选取第三个表格``//*[@id="padding008"]/table[3]/tr[6]``
A[last()]: 选择最后一个元素A
A[last() - 1]: 选择倒数第二个元素A
A[position() < 3]: 选择最前面的两个两个A元素
A[@lang]: 选择包含lang属性的A元素, 返回一个数组
A[@lang='eng']: 选择包含lang属性,并且值为eng的A元素, 返回一个数组
A[price > 35.00]/title: 选取所有A中price值大于35的子元素title
A[@id="abc"]/text(): 获取被标签包含的文本内容
A[@id="abc"]/@href: 获取href属性值

1.4 运算符

  • |: 计算两个节点集, 例如//book | //cd
  • +, -, *, div: 加减乘除运算
  • =/!=: 逻辑运算, 例如A[price!=35.0]
  • >, >=, <, <=: 逻辑运算
  • or, and: 并集与交集运算

2 例子

2.1 获取所有匹配标签的首个

自动解析html字符串, 并匹配某一个标签页数据, 获取第一个包含在ul->li标签中首元素.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from lxml import etree
text = '''
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>
</div>
'''
html = etree.HTML(text)
html.xpath("//ul/li[@class='item-1']")[0]
# 等价于(/表示子元素, //表示包含)
html.xpath("//div//li[@class='item-1']")[0]

直接解析html文件并进行xpath匹配工作

1
2
3
4
from lxml import etree
html = etree.parse('hello.html')
# 获取所有class集合
html.xpath("//ul/li/a//@class")

2.2 获取文章标题

对于如下文本

1
2
3
4
5
<div class="post-110" id="post-110">
<div class="entry-header">
<h1> 我是面试 </h1>
</div>
</div>

相应xpath:

1
2
3
4
5
from lxml import etree
html = etree.parse('hello.html')
# 获取
html.xpath("//div[@class='entry-header']/h1/text()").extract()[0]
html.xpath("//div[@id='post-110']/div[1]/h1/text()").extract()[0]

2.3 匹配含有多个class的某一个值

对于文本, 获取其中的点赞数(包含vote-post-up类)

1
2
3
4
5
6
<div clas="post-adds">
<span class="btn-bluet-bigger href-style vote-post-up register-user-only">
<i class="fa fa-thumbs-0-up"></i>
<h10 id="12353">2</h10>
</span>
</div>

相应的xpath

1
2
3
4
from lxml import etree
html = etree.parse('hello.html')
# 获取
html.xpath("//span[contains(@class, 'vote-post-up')]/h10/text()").extract()[0]

2.4 交集(and)运算

对于某些元素有多个属性的, 需要精准的缩小匹配范围, 需要使用and来提取多个元素

1
2
3
4
5
6
7
8
9
10
11
12
13
# 原始网页
html = '''<div clas="post-adds">
<span class="btn-bluet-bigger href-style vote-post-up register-user-only">
<input type="submit" value="back" class="buttons" name="Something1" />
<input type="submit" value="print" class="bigButtons" name="Something1" />
</span>
</div>
'''
# 希望匹配到back按钮, 则xpath语法:

from lxml import etree
html = etree.HTML(html)
html.xpath('//input[@type="submit" and @value="back" and @class="buttons"]/@name').extract()