XPath(XML Path Language),即 XML 路径语⾔,它是⼀⻔在 XML ⽂档中查找信息的语⾔。最初是⽤来搜寻 XML ⽂档的,但同样适⽤于 HTML ⽂档的搜索,所以在做爬⾍时完全可以使⽤ XPath 做相应的信息抽取。
1. XPath 概览
XPath 的选择功能⼗分强⼤,它提供了⾮常简洁明了的路径选择表达式。另外还提供了超过 100 个内建函数,⽤于字符串、数值、时间的匹配以及节点、序列的处理等,⼏乎所有想要定位的节点 都可以⽤ XPath 来选择。
2. XPath 常用规则
表达式 | 描述 |
nodename | 选取此节点的所有⼦节点 |
/ | 从当前节点选区直接⼦节点 |
// | 从当前节点选取⼦孙节点 |
. | 选取当前节点 |
.. | 选取当前节点的⽗节点 |
@ | 选取属性 |
例如://title[@lang=’eng’],表示的就是选择所有名称为 title,同时属性 lang 的值为 eng 的节点。
3. 安装
pip install lxml
4. 实例
from lxml import etree
# 第一种方式:在 python 代码中解析 html 字符串
# ⽂本中最后⼀个 <li> 节点没有闭合,但是 etree 模块可以⾃动修正 HTML ⽂本
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>
</ul>
</div>
'''
resp_html = etree.HTML(text)
# 第二种方式:读取 html ⽂件并解析
resp_html = etree.parse('./demo.html', etree.HTMLParser())
result = etree.tostring(resp_html)
result.decode('utf-8')
5. 所有节点
# ⽤以 // 开头的 XPath 规则来选取所有符合要求的节点
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>
</ul>
<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>
</ul>
</div>
'''
resp_html = etree.HTML(text)
resp_html.xpath('//li')
6. 子节点
# 通过 / 或 // 即可查找元素的⼦节点或⼦孙节点,直接选择 li 节点的所有直接 a ⼦节点
resp_html.xpath('//li/a')
7. 父节点
# 通过⼦节点查询⽗节点可以⽤ .. 来实现
resp_html.xpath('//a[@href="link4.html"]/..')
# 另一种实现方式
resp_html.xpath('//a[@href="link4.html"]/parent::*')
8. 属性匹配
# 匹配时可以⽤ @ 符号进⾏属性过滤
resp_html.xpath('//li[@class="item-inactive"]')
9. 文本获取
# 第一种方式
resp_html.xpath('//li[@class="item-inactive"]/a/text()')
# 第二种方式:该方法会获取到补全代码时换⾏产⽣的特殊字符,推荐使⽤第⼀种⽅法可以保证获取的结果是整洁的
resp_html.xpath('//li[@class="item-inactive"]//text()')
10. 属性获取
# @ 符号相当于过滤器可以直接获取节点的属性值
resp_html.xpath('//li[@class="item-inactive"]/a/@href')
11. 属性多值匹配
text = '''
<div>
<ul>
<li class="item-0 test"><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>
</ul>
</div>
'''
resp_html = etree.HTML(text)
# 节点的某个属性可能有多个值
resp_html.xpath('//li[contains(@class, "test")]/a/text()')
12. 多属性匹配
text = '''
<div>
<ul>
<li class="item-0 test" name="item"><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>
</ul>
</div>
'''
resp_html = etree.HTML(text)
# 需要同时匹配节点的多个属性
resp_html.xpath('//li[contains(@class, "test") and @name="item"]/a/text()')
XPath 运算符:
运算符 | 描述 | 实例 | 返回值 |
or | 或 | age=18 or age=20 | age=18:True;age=21:False |
and | 与 | age>18 and age<21 | age=20:True;age=21:False |
mod | 计算除法的余数 | 5 mod 2 | 1 |
| | 计算两个节点集 | //book | //cd | 返回所有拥有 book 和 cd 元素的节点集 |
+ | 加法 | 5 + 3 | 8 |
– | 减法 | 5 – 3 | 2 |
* | 乘法 | 5 * 3 | 15 |
div | 除法 | 8 div 4 | 2 |
= | 等于 | age=19 | |
!= | 不等于 | age!=19 | |
< | ⼩于 | age<19 | |
<= | ⼩于等于 | age<=19 | |
> | ⼤于 | age>19 | |
>= | ⼤于等于 | age>=19 |
13. 按序选择
# 匹配结果有多个节点,可以按照中括号内加索引或其他相应语法获得
# 注意:索引值从 1 开始,并非 0
# 获取第一个
resp_html.xpath('//li[1]/a/text()')
# 获取最后一个
resp_html.xpath('//li[last()]/a/text()')
# 获取前两个
resp_html.xpath('//li[position()<3]/a/text()')
# 获取倒数第三个
resp_html.xpath('//li[last()-2]/a/text()')
More function: https://www.w3school.com.cn/xpath/xpath_functions.asp
14. 节点轴选择
# 获取所有祖先节点
resp_html.xpath('//li[1]/ancestor::*')
# 获取 div 祖先节点
resp_html.xpath('//li[1]/ancestor::div')
# 获取当前节点所有属性值
resp_html.xpath('//li[1]/attribute::*')
# 获取 href 属性值为 link1.html 的直接⼦节点
resp_html.xpath('//li[1]/child::a[@href="link1.html"]')
# 获取所有的的⼦孙节点中包含 span 节点但不包含 a 节点
resp_html.xpath('//li[1]/descendant::span')
# 获取当前所有节点之后的第⼆个节点
resp_html.xpath('//li[1]/following::*[2]')
# 获取当前节点之后的所有同级节点
resp_html.xpath('//li[1]/following-sibling::*')