解析库之 XPath(1)

阅读文本大概需要 9 分钟。

自己学习 python 爬虫已经有段时间了,但编程的学习过程总是边学边忘。我也不例外,很多之间学习过的内容,到现在记住的没有几个。所以接下来几周的时间,我会把爬虫各个基础知识点全部在巩固一遍。

我的计划是通过《python3 网络爬虫开发实战》这本书,写下自己的读书笔记。并且在最后我会把今天文章所学到的内容,整理成一个思维导图,方便大家理清知识点,也方便自己日后复习。话不多说,这周让我们一起来学习下 python 爬虫很重要的一个模块「解析库的使用」。

Python 常用解析库

解析库顾名思义就是解析某个特定的内容,一个爬虫的思路非常的简单,即向服务器发起请求,我们得到响应之后,根据返回的内容做进一步处理。一般返回的内容是网页的源代码,有时候还会是 json 数据。针对网页源代码,我们就需要利用解析库来解析到我们想要的内容。

而常用的解析库有三种:

1 XPath

2 Beautiful Soup

3 pyquery

每种解析库都可以把网页的数据解析出来,这三种解析库最常用的就是 XPath。所以今天我们就详细介绍下 XPath 解析库的使用,此教程一共分为三篇,今天是第一篇。

XPath

学习过 web 相关知识的同学应该知道,前端界面是由很多个节点构建而成的,它可以定义 id、class 或其他属性。而且节点之间还有层次关系,在网页中我们就可以通过 XPath 或 CSS 选择器来定位一个或多个节点。

那什么是 XPath?

XPath 全称 XML Path Language,即 XML 路径语言,它是一门 XML 文档中查找信息的语言。它最初是用来搜寻 XML 文档的,但是它同样适用于 HTML 文档的搜索。

所以在做爬虫时,我们完全可以使用 XPath 来做相应的信息抽取。接下来我们就来介绍下 XPath 的基本用法。

1. XPath 概况

XPath 的选择功能十分强大,它提供了非常简洁明了的路径选择表达式。另外,它还提供了超过 100 个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等。几乎所有我们想要定位的节点,都可以用 XPath 来选择。

XPath 更多资料可以访问其官网:

https://wwww.w3.org/TR/xpath/

2. XPath 常用规则

这个表格非常重要,里面每个表达式所代表的含义,大家一定要熟记于心。如果你不懂每个表达式的含义,就很难写出正确的 XPath 表达式,相应的数据就无法获取到。

举个例子:

//title[@lang='eng']

这就是一个 XPath 规则,它代表选择所有名称为 title,同时属性 lang 的值为 eng 的节点。

后面会通过 Python 的 lxml 库,利用 XPath 进行 HTML 的解析。

3. lxml 安装

使用 XPath 之前,我们需要安装 lxml 库。安装的方法也非常简单,有两种方式。

1 pip install lxml

2 在 https://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml 这个网站里下载你对应电脑版本的 whl 文件,然后通过「pip install 文件路径」方式进行安装。

4. 实例引入

现在通过实例来感受一下使用 XPath 来对网页进行解析的过程,相关代码如下:

from lxml import etree

text = '''

first item

second item

third item

fourth item

fifth item

'''

html = etree.HTML(text)

result = etree.tostring(html)

print(result.decode('utf-8'))

这里首先导入 lxml 库的 etree 模块,然后声明了一段 HTML 文本,调用 HTML 类进行初始化,这样就成功构造了 XPath 解析对象。这里需要注意的是,HTML 文本中的最后一个 li 节点是没有闭合的,但是 etree 模块可以自动修正 HTML 文本。

这里我们调用 tosting() 方法即可输出修正后的 HTML 代码,但是结果是 bytes 类型。这里利用 decode() 方法将其转成 str 类型,结果如下:

可以看到经过处理之后,li 节点标签被补全,并且还自动添加了 body、html 节点。

另外,也可以直接读取文本文件进行解析:

fromlxmlimportetree

html = etree.parse('test.html', etree.HTMLParser())

result = etree.tostring(html)

print(result.decode('utf-8'))

其中 test.html 代码就是上面的代码

这次的输出结果略有不同,多了一个 DOCTYPE 的声明,不过对解析无任何影响,结果如下:

5. 所有节点

我们一般会用 // 开头的 XPath 规则来选取所有符合要求的节点。这里以前面的 HTML 文本为例,如果要选取所有节点,可以这样实现:

fromlxmlimportetree

html = etree.parse('test.html', etree.HTMLParser())

result = html.xpath('//*')

print(result)

运行结果如下:

这里使用 * 代表匹配所有节点,也就是整个 HTML 文本中的所有节点都会被获取。可以看到,返回形式是一个列表,每个元素是 Element 类型,其后跟了节点的名称,如 html、body、div、ul、li、a 等,所有节点都包含在列表中了。

当然,此外匹配也可以指定节点名称。如果想获取所有 li 节点,代码如下:

fromlxmlimportetree

html = etree.parse('test.html', etree.HTMLParser())

result = html.xpath('//li')

print(result)

这里要选取所有 li 节点,可以使用 //,然后直接加上节点名称即可,调用时直接用 xpath() 方法即可。

运行结果:

这里可以看到提取结果是一个列表形式,其中每个元素都是一个 Element 对象。如果要取出其中一个对象,可以直接用中括号加索引,如[0]。

6. 子节点

我们通过 / 或 // 即可查找元素的子节点或子孙节点。假如现在想选择 li 节点的所有直接 a 子节点,可以这样实现:

fromlxmlimportetree

html = etree.parse('test.html', etree.HTMLParser())

result = html.xpath('//li/')

print(result)

这里通过追加 /a 即可选择所有 li 节点的所有直接 a 子节点。因为 //li 用于选中所有 li 节点,/a 用于选中所有子孙节点 a,二者组合在一起获取所有 li 节点的所有直接 a 子节点。

运行结果:

此处的 / 用于选取直接子节点,如果要获取所有子孙节点,就可以使用 // 。例如,要获取 ul 节点下的所有子孙 a 节点,可以这样实现:

fromlxmlimportetree

html = etree.parse('test.html', etree.HTMLParser())

result = html.xpath('//li//a')

print(result)

运行结果是相同的。

但是如果这里用 //ul/a,就无法获取任何结果了。因为 / 用于获取直接子节点,而在 ul 节点下没有直接的 a 子节点,只有 li 节点,所以无法获取任何匹配结果,代码如下:

fromlxmlimportetree

html = etree.parse('test.html', etree.HTMLParser())

result = html.xpath('//ul/a')

print(result)

运行结果为空:

因此,这里我们要注意 / 和 // 的区别,其中 / 用于获取直接子节点,// 用于获取子孙节点。

总结

至此今天我们介绍了什么是 XPath,XPath 有什么用,以及 XPath 如何使用。但 XPath 相关用法还没全部讲解完,一篇文章不应该过长。这样不利于你理解消化,所以 XPath 的教程,我打算分 3 篇文章来讲解。今天这是第一篇,主要带大家认识下 XPath 的一些基本操作,接下来的第二篇详细介绍 XPath 的操作,第三篇则是带大家一起来实战练习。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180717A09YQM00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券