Xpath的使用
XPath 的全称是 XMLPathLanguage,即 XML 路径语言,用来在 XML 文档中查找信息。它虽然最初是用来搜寻 XML 文档的,但同样适用于 HTML 文档的搜索。
所以在做爬虫时,我们完全可以使用 XPath 实现相应的信息抽取。本节我们就介绍一下 XPath 的基本用法。
XPath概览
XPath 的选择功能十分强大,它提供了非常简洁明了的路径选择表达式。另外,它还提供了 100 多个内建函数,用于字符串、数值时间的匹配以及节点、序列的处理等。几乎所有我们想要定位的节点,都可以用 XPath 选择。
XPath 于1999年11月16日成为 W3C 标准,它被设计出来,供 XSLI、XPointer 以及其他 XML 解析软件使用。
XPath常用规则
表 3-1 列举了 XPath 的几个常用规则。
表达式 | 描述 |
---|---|
nodename |
选取此节点的所有子节点 |
/ |
从当前节点选取直接子节点 |
// |
从当前节点选取子孙节点 |
. |
选取当前节点 |
.. |
选取当前节点的父节点 |
@ |
选取属性 |
这里列出了 XPath 的一个常用匹配规则,如下:
//title[@lang='eng']
它代表选择所有名称为 title,同时属性 lang 的值为 eng 的节点。
后面会通过 Python 的 lxml 库,利用 XPath 对 HTML 进行解析。
准备工作
使用 lxml 库之前,首先要确保其已安装好。
可以使用 pip3 来安装:
pip3 install lxml
更详细的安装说明可以参考: https://setup.scrape.center/lxml 。
安装完成后,就可以进入接下来的学习了。
实例引入
下面通过实例感受一下使用 XPath 对网页进行解析的过程,相关代码如下:
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>
</ul>
</div>
'''
html = etree.HTML(text)
result = etree.tostring(html)
print(result.decode('utf-8'))
这里首先导入 lxml 库的 etree 模块,然后声明了一段 HTML 文本,接着调用 HTML 类进行初始化,这样就成功构造了一个 XPath 解析对象。此处需要注意一点,HTML 文本中的最后一个 li 节点是没有闭合的,而 etree 模块可以自动修正 HTML 文本。
之后调用 tostring 方法即可输出修正后的 HTML 代码,但是结果是 bytes 类型。于是利用 decode 方法将其转换成 str 类型,结果如下:
<html><body><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></body></html>
可以看到,经过处理之后的 li 节点标签得以补全,并且自动添加了 body、html 节点。
另外,也可以不声明,直接读取文本文件进行解析,实例如下:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = etree.tostring(html)
print(result.decode('utf-8'))
其中 test.html 的内容就是上面例子中的 HTML 代码,内容如下:
<html><body><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></body></html>
这次的输出结果略有不同,多了一个 DOCTYPE 声明,不过对解析无任何影响,结果如下:
所有节点
我们一般会用以 // 开头的 XPath 规则,来选取所有符合要求的节点。这里还是以第一个实例中的 HTML 文本为例,选取其中所有节点,实现代码如下:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//*')
print(result)
运行结果如下:
这里使用 * 代表匹配所有节点,也就是获取整个 HTML 文本中的所有节点。从运行结果可以看到,返回形式是一个列表,其中每个元素是 Element 类型,类型后面跟着节点的名称,如 html、body、div、u1、li、a 等,所有节点都包含在了列表中。
当然,此处匹配也可以指定节点名称。例如想获取所有 li 节点,实例如下:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li')
print(result)
print(result[0])
这里选取所有 li 节点,可以使用 //,然后直接加上节点名称,调用时使用 Xpath 方法即可。
运行结果如下:
可以看到,提取结果也是一个列表,其中每个元素都是 Element 类型。要是想取出其中一个对象,可以直接用中括号加索引获取,如[0]。