一、前言
因为作者我最近参加了那个CSDN21天学习挑战赛,导致有关Python爬虫的博客有一段时间没有更新内容啦,实在是抱歉。
今天我们要来学习有关XPath的使用知识,上一期爬虫博客我们进行了一次基础的爬虫实战训练,不知道大家还有没有印象,我把我的爬虫专栏地址放在下面了,期待大家的点赞关注!
我的学习专栏:Python爬虫学习
在上期我们在提取页面信息的时候直接使用的是正则表达式,过程很繁琐而且不太容易理解,一般爬虫代码中很少直接使用正则表达式进行爬取,而且采取各种Python库来简化操作,我们有很多有关这类的库,我们首先介绍其中的一个,它就是今天要说的XPath库。
二、我的环境
- 电脑系统:Windows 11
- 语言版本:Python 3.10
- 编译器:PyCharm 2022.2
三、XPath的使用
1、准备工作
学习一个库的开始当然就是首先需要安装这个库到我们的编译器里面。
使用如下命令就可以将lxml库安装到我们的编译器里面了:
pip install lxml
lxml本身就是一个功能特别强大的第三方模块,在这个模块里面有一个etree类可以将网页的源代码示例化为一个etree对象,这个对象支持使用XPath表达式进行标签的定位,这样就更加方便我们从HTML各种标签内提取我们需要的数据了。
这里需要注意一个问题,一般按照上面的pip安装会安装最新的lxml,但是我们还是需要注意一下,lxml一些老版本内并没有etree类,如果你很早之前使用过过lxml或者安装成了老版本最好重新安装一些,建议安装4.5版本以上的lxml模块。
可以在这里查看自己安装的版本是多少:
2、自动修正HTML文本
from lxml import etree text = ''' <div> <ul> <li class="item1"><a href="link1.html">massage1</a></li> <li class="item2"><a href="link2.html">massage2</a></li> <li class="item-inactive"><a href="link3.html">massage3</a></li> <li class="item2"><a href="link4.html">massage4</a></li> <li class="item1"><a href="link5.html">massage5</a> </ul> </div> ''' html = etree.HTML(text) result = etree.tostring(html) print(result.decode('utf-8'))
可以看见最后一个li节点并没有闭合,但是etree模块它可以自动修正补全HTML文本,之后调用tostring方法返回的结果是bytes类型,再利用decode方法将其转换成str类型。
它运行的结果是:
<html><body><div> <ul> <li class="item1"><a href="link1.html">massage1</a></li> <li class="item2"><a href="link2.html">massage2</a></li> <li class="item-inactive"><a href="link3.html">massage3</a></li> <li class="item2"><a href="link4.html">massage4</a></li> <li class="item1"><a href="link5.html">massage5</a> </li></ul> </div> </body></html>
可以看出它自定补全了li节点并且添加了body和html节点。
3、XPath常用规则
在学习如何获取各种节点信息之前我们先了解一些XPath的常用规则,后续会用到。
表达式 | 说明 |
nodename | 获取此节点的所有子节点 |
/ | 从当前节点获取直接子节点 |
// | 从当前节点获取子孙节点 |
. | 获取当前节点 |
… | 获取当前节点的父节点 |
@ | 获取属性 |
4、获取所有节点
from lxml import etree html = etree.parse('./html文本.html', etree.HTMLParser()) result = html.xpath('//*') print(result) print(result[0])
它运行的结果是:
[<Element html at 0x16b8f9c3000>, <Element body at 0x16bb0e075c0>, <Element div at 0x16bb0e07100>, <Element ul at 0x16bb0e07580>, <Element li at 0x16bb0e07740>, <Element a at 0x16bb0e07800>, <Element li at 0x16bb0e076c0>, <Element a at 0x16bb0e07900>, <Element li at 0x16bb0e07940>, <Element a at 0x16bb0e07840>, <Element li at 0x16bb0e07980>, <Element a at 0x16bb0e079c0>, <Element li at 0x16bb0e07a00>, <Element a at 0x16bb0e07a40>] <Element html at 0x16b8f9c3000>
这里使用//*来匹配所有的节点,它返回的是一个element类型,是一个列表,可以使用[索引值]来获取对应的节点,也可以将它写成其他你想要匹配的节点,例如li节点,他就会返回所有li的节点。
5、获取子节点
假如我们要选取li节点下的a节点,我们就可以这样来写:
from lxml import etree html = etree.parse('./html文本.html', etree.HTMLParser()) result = html.xpath('//li/a') print(result)
它运行的结果是:
[<Element a at 0x257f0d162c0>, <Element a at 0x257f0d17300>, <Element a at 0x257f0d16a00>, <Element a at 0x257f0d17280>, <Element a at 0x257f0d17480>]
//li/a是获取li节点下的所有直接子节点,而//li//a就是获取li节点下的所有子孙节点。
6、获取父节点
例如我们需要获取href属性为link2.html的a节点的父节点
from lxml import etree html = etree.parse('./html文本.html', etree.HTMLParser()) result = html.xpath('//a[@href="link2.html"]/..') print(result)
它运行的结果是:
[<Element li at 0x274476e7540>]
也可以改成这样,结果是一样的:
result = html.xpath('//a[@href="link2.html"]/parent::*')
7、获取属性值
XPath获取属性值的公式就是:
//标签1[@属性1="属性值1"]/标签2[@属性2="属性值2"]/.../@属性n
例如我们要获取所有href的属性值:
from lxml import etree html = etree.parse('./html文本.html', etree.HTMLParser()) result = html.xpath('//li/a/@href') print(result)
它运行的结果是:
['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']
可以看到我们返回了所有li节点下的a节点中的href属性值。
8、获取文本
XPath获取文本的公式就是:
//标签1[@属性1="属性值1"]/标签2[@属性2="属性值2"]/.../text()
例如我们来获取一些li节点下a节点内的文本信息:
from lxml import etree html = etree.parse('./html文本.html', etree.HTMLParser()) result = html.xpath('//li/a/text()') print(result)
它运行的结果是:
['massage1', 'massage2', 'massage3', 'massage4', 'massage5']
如果要获取某一属性下的文本的话需要添加上对应的属性匹配,例如我们要获取li节点class属性值为item-inactive下的文本就可以修改成:
result = html.xpath('//li[@class="item-inactive"]/a/text()')
这样就可以匹配对应的文本信息了。
9、属性多值匹配
有时候会遇见一些节点它的属性可能有多个值,下面举两个例子进行说明一下。
第一种情况:
<li class="li li-first"><a href="link.html">massage</a></li>
可以看出li节点的class属性有两个值,这个时候我们就不能使用之前的方法来获取信息了,这个时候我们需要使用contains方法,contain方法第一个参数是属性名称,第二个参数就是属性值。
from lxml import etree text = ''' <li class="li li-first"><a href="link.html">massage</a></li> ''' html = etree.HTML(text) result = html.xpath('//li[contains(@class, "li")]/a/text()') print(result)
它运行的结果是:
['massage']
第二种情况:
<li class="li li-first" name="item"><a href="link.html">massage</a></li>
这里li节点除了有class属性,还有一个name属性,因此我们要确定li节点,就需要将class属性和name属性都考虑在内才行,这个时候就需要使用and来连接多个属性。
result = html.xpath('//li[contains(@class, "li") and @name="item"]/a/text()')
10、按序选择
在有些时候会有一些重复的节点,例如我们举例代码中就有很多li节点,但是如果我们只想要其中某一个节点的信息,这就需要使用中括号传入索引的方法来获取特定次序的节点了。
from lxml import etree html = etree.parse('./html文本.html', etree.HTMLParser()) result1 = html.xpath('//li[1]/a/text()') print(f"选择第一个li节点:", result1) result2 = html.xpath('//li[last()]/a/text()') print(f"选择最后一个li节点:", result2) result3 = html.xpath('//li[last()-2]/a/text()') print(f"选择倒数第三个li节点:", result3) result4 = html.xpath('//li[position()<3]/a/text()') print(f"选择位置小于3的li节点:", result4)
它运行的结果是:
选择第一个li节点: ['massage1'] 选择最后一个li节点: ['massage5'] 选择倒数第三个li节点: ['massage3'] 选择位置小于3的li节点: ['massage1', 'massage2']
以上我只举了几个例子,XPath还提供了超过100中方法,这些方法我就简单的举例几个,剩下的还有很多大家可以自行上网了解学习。
四、快速获取标签节点的XPath表达式
XPath表达式相比于直接使用正则表达式要简单很多,但有时候编写的时候还是要花点时间去组合,这里我来提供一种可以快速获取XPath表达式的方法,绝对能让你事半功倍。
这个方法就是使用谷歌浏览器的开发者工具的右键快捷菜单命令就可以快速的获取XPath表达式了,具体方法如下:
我们在谷歌浏览器打开任意一个网页,然后按下键盘上面的F12,就会弹出开发者工具,然后点击Elements选项,然后选择你想要获取XPath表达式的HTML代码,右击选择Copy然后选择Copy XPath就可以了,之后就可以将复制的XPath表达式粘贴到爬虫代码中就可以实现了。
是不是非常的方便快捷,省去了大量自己思考的时间,哈哈哈,提高效率首选。
五、最后我想说
关于XPath选择器一些基础的知识大致总结完毕了,剩下有关它的其他功能和用法大家可以自行去官网文档里面了解并学习。
总之,XPath功能非常的强大,搭配上谷歌浏览器的开发者工具可以快速的获取HTML源代码内你想要的信息,非常的方便,学习爬虫的小伙伴吗们尽管放心的去熟悉它的使用方法,这会大大的提高提取HTML信息的效率。
下一期博客我会继续总结下一个强大的库——Beautiful Soup的一些基本使用方法,和大家一起学习。
以上文章如有错误之处,还请大家为我指出,谢谢!
最后,创作不易,期待大家的点赞收藏转发三连,你们的鼓励将是我创作最大的动力。