XPath(XML Path Language):XML路径语言,它是一门在XML文档中查找信息的语言,它最初是用来搜寻XML文档的,但是它同样适用于HTML文档的搜索。XPath的选择功能十分强大,它提供了非常简明的路径选择表达式,另外,它还提供了超过100个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等,几乎所有我们想要定位的节点,都可以用XPath来选择
1.XPath
的解析原理
使用lxml
库的etree
对象来将需要被解析的HTML
页面源码数据加载到该对象中,通过etree
对象中的XPath
方法结合其表达式来实现标签定位和内容捕获。
2.基本使用方法
- 首先通过
pip install lxml
来安装lxml
库,该库是Python
的一个解析库,支持HTML
和XML
的解析,支持XPath
解析方式同时解析效率非常高。 - 将获取的
HTML
文档中的源码数据加载到etree
对象中,一般来说都是结合爬虫使用。
#定义请求URL
url='http://www.baidu.com'
#定义请求头
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.42'}
#向指定的URL发送request请求
req = urllib.request.Request(url=url, headers=headers)
#根据request请求获得response
res = urllib.request.urlopen(req)
#使用gbk编码对该结果进行读取
html = res.read().decode('gbk')
#实例化etree对象
etree.HTML(html)
注意:如果加载的是本地的文件那么可以使用etree.parse(filePath)
来解析得到selector
上述解析得到的selector
对象实则是Python
的Element
类,打印该对象会得到类似于[<Element div at 0x19d447658c8>]
的结果,要切实获得其中的内容的话需要使用text()
或string(.)
3.XPath
定位节点的方法
该部分主要介绍的是XPath
用来定位节点所使用的表达式
nodename | 选取此节点的所有子节点 |
---|---|
/ | 表示的是从根节点开始定位。表示的是一个层级。 |
// | 表示的是多个层级。可以表示从任意位置开始定位。 |
. | 选取当前节点 |
… | 选取当前节点的父节点 |
@ | 选取属性 |
* | 通配符,选择所有元素节点与元素名 |
@* | 选取所有属性 |
[@attrib] | 选取具有给定属性的所有元素 |
[@attrib=‘value’] | 选取给定属性具有给定值的所有元素 |
[tag] | 选取所有具有指定元素的直接子节点 |
[tag=‘text’] | 选取所有具有指定元素并且文本内容是text节点 |
需要补充的是XPath
也可以通过位置来定位节点:
- 使用索引的方式来选择特定的节点:如
//ul[2]/li/text()
- 使用谓语的方式来选择特定的节点:如
//ul[last()]/li/text()
text = '''<title>标题</title>
<body>
<ul class='list1'>
<li>列表1第1项</li>
<li>列表1第2项</li>
</ul>
<p class='first'>文字1</p>
<p class='second'>文字2</p>
<ul class='list2'>
<li>列表2第1项</li>
<li>列表2第2项</li>
</ul>
</body>'''
html = etree.HTML(text)
html.xpath('//ul[2]/li/text()') # 在xpath语句中使用索引,限制只在第二个ul中寻找
html.xpath('//ul/li[1]/text()') # 在每个ul中取第一个li
# ['列表2第1项', '列表2第2项']
html.xpath('//ul[last()]/li/text()') # 取最后一个
html.xpath('//ul[last()-1]/li/text()') # 倒数第二个
html.xpath('//ul[position()<3]/li/text()') # 前两个
4.Xpath
提取内容的方法
XPath
主要使用两个方法提取某个确定节点的内容:
- 提取标签的文本内容:使用
/text()
- 提取标签的属性值:使用
/@属性名
备注:执行的每一行XPath
代码的输出在同行的注释中体现。
text = '''
<body>
<h><a href='www.biaoti.com'>head</a></h>
<p>段落1</p>
<p>段落2</p>
</body>
'''
html = etree.HTML(text)
html.xpath('//h') # [<Element h at 0x2122e64e4c8>]
# 用//text() 提取标签内容
html.xpath('//h//text()') # ['head']
# /text()和//text()的区别在于不可以提取孙节点的内容
html.xpath('//h/a/text()') # ['head']
# xpath语法默认提取全部
html.xpath('//p/text()') # ['段落1', '段落2']
# 提取标签属性
html.xpath('//h/a/@href') # ['www.biaoti.com']
这里需要注意的是不论当前XPath
搜索到的节点是单个还是多个都会将其封装到list
中,如果想要获得具体的字符串值的话就需要通过索引进行访问。
此外,XPath
库中提供了一个使用方法string(.)
来代替text()
完成特定的需要,由于后者所获得的内容是列表,因此如果想要将列表中的每个元素进行拼接的话不得不遍历列表才能实现,而前者可以直接将所有元素拼接起来转换为字符串形式,方便我们的使用。
4.XPath
的使用规律总结
text = '''<title>标题</title>
<body>
<ul class='list1'>
<li>列表1第1项</li>
<li>列表1第2项</li>
</ul>
<p class='first'>文字1</p>
<p class='second'>文字2</p>
<ul class='list2'>
<li>列表2第1项</li>
<li>列表2第2项</li>
</ul>
</body>'''
from lxml import etree
html = etree.HTML(text)
html.xpath('//title/text()')[0] # '标题'
html.xpath("//p[@class='first']//text()")[0] # '文字1'
html.xpath("//p/text()") # ['文字1', '文字2']
html.xpath("//ul[@class='list1']/li/text()") # ['列表1第1项', '列表1第2项']
根据上述输出结果我们可以简单总结出XPath
语法的规律:
- 用
XPath
语法提取基于节点路径时只要输入一个字符串该字符串按照格式填写就能完成提取 - 格式是一层层的节点,用
/
分离,通过[]
在后面加入该节点的属性判断来唯一确定节点位置 - 提取的结果永远是
list
,最后都要用索引提取 - 不特殊指定的情况下会自动寻找所有满足条件的结果,可以在提取结果中用索引筛选,也可以在
XPath
内部使用索引 - 语法中分隔节点有两种方法
/
和//
,它们之间的差别在于,前者只寻找子节点,后者会寻找子孙所有后代的节点,将所有满足条件的全都找到 - 一般来说先使用
//
是为了快速定位我们要找的位置的节点此后即可在基础上进行扩展和发挥