3.2 XPath 语法基础
3.2.1 Lxml 库的安装

爬虫基础-Xpath 语法详解
Lxml 库是效率非常高且简单易学的网页解析库,是本书所推荐读者首先要学习的网页解析库。Lxml 库不是 Python 标准库,需要自行安装。可以在命令行里面用 pip 命令来安装 Lxml 库。
>pip install lxml
在 Windows 平台使用上面的命令安装,可能会出现各种各样的问题,这里为读者提供在 Windows 平台上安装 Lxml 库的两种方法。
1. 直接下载二进制 whl 安装包(推荐)
第一步,从 http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml 下载与系统位数、Python 版本对应的 whl 安装包。
第二步,假如下载目录为 D:\Downloads,使用如下命令安装(注意文件名替换成实际下载的文件名)。
>pip install D:\Downloads\lxml-4.2.1-cp36-cp36m-win_amd64.whl
这样就可以成功在 Windows 上安装 Lxml 了。
上述下载 whl 安装包的网址提供了非常多的 Windows 二进制 Python 安装包,在 Windows 平台安装 Python 包遇到问题的时候,可以尝试在这里下载安装包并安装。还有一点要注意,要下载与 Windows 位数及安装的 Python 版本对应的安装包。例如,Windows 是 64 位的,安装了 Python 3.6.3,要下载的版本为 lxml-4.2.1-cp36-cp36mwin_amd64.whl。
2. 直接安装 Anaconda 的 Python 发行版本
Anaconda 默认会安装 Lxml,可以从其官网下载与自己的系统位数相对应的 Anaconda 安装包,下载完成后直接双击安装即可。Anaconda 非常适合从事数据分析人员安装使用。
3.2.2 XPath 语法基础 - 通过路径查找元素
XPath 即为 XML 路径语言,它是一种用来确定 XML 文档中某部分位置的语言。XPath 基于 XML 的树状结构,有不同类型的节点,包括元素节点、属性节点和文本节点,提供在数据结构树中寻找节点的能力。XPath 通常被开发者用来当作小型查询语言。下面用例子来演示 XPath 的用法。
首先导入 Requests 和 lxml.etree 这两个模块。
>>>import requests >>>from lxml import etree
Lxml 大部分功能都存在于 lxml.etree 下,我们主要使用 etree 这个模块,所以这里从 Lxml 导入 etree。
然后定义一个 HTML 源码作为例子,示例如下。

爬虫基础-Xpath 高级用法
htm="""
...<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>
... <li class="else-0">else item</li>
another item
... </ul>
...</div>
...""" 这段 HTML 源码的最外层是一对<div>标签,里面一层是一对<ul>标签,<ul>标签里有六对<li>标签,前五对<li>标签每一个又包含着一对<a>标签。这就是本段 HTML 源码的结构,它的这种结构是一层一层的,如果想找第一个<li>标签,可以从头开始找:先找到<div>→然后找<ul>→最后找第一个<li>。
XPath 语法实际上就是使用这种层级的路径来找到相应元素的,它非常类似于人们日常使用的地址,它们都是从大的范围一直缩小到具体的某个地址上。当然了,如果要找的某个元素或地址是独一无二的,可以直接指明这个地址,不需要用这种层级关系来一层一层地定位。XPath 对这种独一无二的元素可以直接定位。下面具体用代码来实现元素的查找和定位。
首先需要使用 HTML 源码初始化 etree。
>>>selector = etree.HTML(htm)
这样就得到了一个名字叫作 selector 的 Element 对象,可以对这个 Element 对象使用 XPath 筛选,系统会返回一个筛选的结果列表。
先查找所有的<li>标签。XPath 使用//来表示从根节点开始查找。
>>>all_li = selector.xpath('//div/ul/li')上面的代码对 selector 这个 Element 对象使用 XPath 方法,方法的参数就是 XPath 路径,这个路径是一个字符串的形式,上面代码中'//div/ul/li'这个 XPath 路径可以这样理解:从根节点开始查找<div>,然后寻找<div>下面的<ul>标签,最后在<ul>下面找到所有的<li>标签。
这样就查找到了所有的<li>标签,可以打印一下查看。
>>>print(all_li) [<Element li at 0x72711c8>, <Element li at 0x7271148>, <Element li at 0x7271108>, <Element li at 0x72710c8>, <Element li at 0x7271088>, <Element li at 0x7271648>]
这是一个所有<li>标签的列表。如果想定位第一个<li>标签,可以编写如下代码。
>>>li_1 = selector.xpath('//div/ul/li[1]')这里还是使用了路径查找,只是指定了<li>标签的序号,使用 li[1]这种形式,说明要提取的是第一个<li>,要特别注意,XPath 语法中的序号是从 1 开始的,与 Python 切片语法中从 0 开始切片是不一样的。还要注意,因为<div>标签和<ul>标签都只有一个,所以在查找和定位的时候没有使用序号。
前面已经找到了第一个<li>标签,现在要提取第一个<li>标签下的<a>标签里面的文本,可以在路径中使用 text() 这种方法提取文本信息。
>>>li_1_a_text = selector.xpath('//div/ul/li[1]/a/text()')
>>>print(li_1_a_text)
['first item'] 上面的代码还是按照路径一步步找到<a>,然后使用 text() 提取出其中的文本信息,提取出来的信息是只有一个元素的列表,可以对这个列表切片,直接取出其中的文本内容。读者一定要学会使用 text() 提取文本信息。
读者可能会注意到,既然在这段源码里面<ul>标签是唯一的,可以直接从根节点去定位<ul>标签,这样不会有任何歧义,因此提取第一个<li>标签里面<a>标签的文本也可以按如下形式编写。
>>>li_1_a_text = selector.xpath('//ul/li[1]/a/text()')[0]
>>>print(li_1_a_text)
'first item' 3.2.3 通过属性查找元素
下面来看如何使用属性查找元素。
在源码中有<li class="item-0">这种形式的代码,这里的 class 就是一个<li>标签的属性,同样道理,<a href="link1.html">里的 href 是<a>标签的一个属性。可以通过属性定位元素。
例如要找第三个<li>标签,第三个<li>元素的属性是 class="item-inactive",可以编写如下代码来定位它。
>>>li_3 = selector.xpath('//ul/li[@class="item-inactive"]')利用属性来定位元素要使用类似[@class="item-inactive"]这种语法形式。再看一下 HTML 源码,这个属性在整个 HTML 源码里是唯一的,因此可以从根目录直接根据这个属性定位到第三个<li>标签。
>>>li_3 = selector.xpath('//*[@class="item-inactive"]') 上面的 XPath 路径中的*代表任意的标签,这个路径的含义就是:从根节点开始查找 class 属性等于 item-inactive 的任意标签,因为这个属性是唯一的,所以直接就定位到了第三个<li>标签处。这个时候如果想取出里面<a>标签下的文本,可以编写如下代码。
>>>li_3_a_text = selector.xpath('//*[@class="item-inactive"]/a/te xt()')既可以在单引号里面使用双引号,也可以在双引号里面使用单引号,但是同时使用单引号和双引号会出现问题。上面使用了<li>的 class 属性来定位元素,同样可以使用<a>标签的 href 属性来定位,效果是完全相同的。
>>>li_3_a_text = selector.xpath('//a[@href="link3.html"]/text()') 3.2.4 提取属性值
提取属性值就是说,想要提取的内容是某个标签里的某个属性的值,例如要提取第三个<li>里的<a>标签的 href 属性值,可以编写如下代码。
>>>li_3_a_href = selector.xpath('//ul/li[3]/a/@href') 这里通过 @href 这种语法形式提取到了<a>标签的 href 属性值。
最后编写如下代码取出所有<li>标签的 class 属性。
>>>all_class = selector.xpath('//li/@class') 这里从根节点开始查找<li>标签,但是有很多个<li>标签,路径中没有使用序号指明是哪一个<li>标签,那就代表要提取的是全部的<li>标签,然后使用 @class 取出所有<li>标签的 class 属性值。
3.2.5 XPath 的高级用法
HTML 源码共有 6 个<li>标签,现在假设要提取前 5 个<li>标签,前 5 个<li>标签的特点:虽然它们的 class 属性不尽相同,但都是以 item-开头的。可以利用这个特点,提取出前 5 个<li>标签。
>>>li_1_5 = selector.xpath('//li[starts-with(@class,"item-")]') 这里使用 starts-with 这样一种语法形式,成功地定位到了所有 class 属性以 item-开头的<li>标签。
如果提取出来的元素里面包含着子元素或者说提取出来的是一个代码段,可以对它继续使用 XPath 查找。例如上面提取了前 5 个<li>标签,实质上是 5 个代码段,因为这些<li>标签里还都包含着<a>标签,可以对提取出来的<li>标签继续使用 XPath 方法,查找里面的文本内容。
>>>li_1_5_a_text = []
>>>for li in li_1_5: li_1_5_a_text.append(li.xpath('a/text()')[0]) 这里使用了一个 for 循环,对提取出来的前 5 个<li>标签的 HTML 代码分别继续使用 XPath 方法,通过相对路径 a/text() 查找出了它们包含的<a>标签里面的文本信息。也可以使用 .//a/text() 这样的相对路径来查找,.// 表示以当前元素为根节点向下查找。
上面是为了说明对提取出来的代码段可以继续使用 XPath 方法,其实如果提取前 5 个<li>标签下<a>标签里的文本,没必要使用 for 循环,可以直接编写如下代码。
>>>li_1_5_a_text = selector.xpath('//li[starts-with\
(@class,"item-")]/a/text()') 结果与上面是完全相同的。
来看最后一个例子:如何从代码段中提取出所有的文本。现在假设想要提取<ul>里各层级下全部的文本。这段 HTML 源码的特点:<a>标签里、最后一个<li>标签里,甚至<ul>标签里都包含有文本信息。如果使用如下代码只能提取到<ul>这一层本层的文本。
>>>ul_text = selector.xpath('//ul/text()') 要提取<ul>里各层级下全部的文本,可以使用下面的代码。
>>>all_text = selector.xpath('string(//ul)') 这里在路径的外面添加了 string(),这样就成功地提取出<ul>标签里所有的文本信息。然后可以使用列表推导式取出所有的文本。
>>>[s.strip() for s in
all_text.strip().split('\n')]
['first item',
'second item',
'third item',
'fourth item',
'fifth item',
'else item',
'another item'] 以上使用了一段 HTML 代码作为例子,为读者详细讲解了使用 XPath 提取数据的各种技巧和方法。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论