安装与导入
xpath 功能是在 lxml 库中,需要安装 lxml 库。
> pip install lxml
使用时要从 lxml 库中导入 etree 模块:
from lxml import etree
前置代码
xpath解析需要使用一段 xml或html进行演示,在此小节导入模块与定义 html片段:
from lxml import etree
html = """
<html>
<head></head>
<body>
<div>
<span>电影</span>
<div class="movie" id="movie1">
<div class="title">肖申克的救赎</div>
<div class="director">弗兰克·德拉邦特</div>
<div class="year">1994</div>
<div class="grade">9.7</div>
</div>
<div class="movie" id="movie2">
<div class="title">霸王别姬</div>
<div class="director">陈凯歌</div>
<div class="year">1993</div>
<div class="grade">9.6</div>
</div>
</div>
</body>
</html>
"""
element_obj = etree.HTML(html)
xpath知识点总结
/ 斜杠符号,表示当前节点的子层级,若是在开头,则表示根节点的子层级
* 匹配当前层级的任意节点
// 当前节点的所有子节点,包含子节点的子节点以及无限层级的子节点
./ 一般用在开头,表示当前节点,与子节点对象配合使用
节点[@属性名] 获取有指定来这个属性的节点
节点[@属性名="值"] 获取指定属性值的节点
节点[从1开始的数字] 获取指定索引的节点
节点[last()] 获取最后一个节点
节点[last()-1] 获取倒数第二个节点
节点[position()<3] 获取前两个节点
text() 获取当前节点中的内容
@属性名 获取节点的属性的值
通过叙述的方式学习xpath
假设我们要获取电影
二字,那么可以:
print(element_obj.xpath('/html/body/div/span/text()'))
# 结果为:['电影']
过程为:第一个/
表示从根开始查找,之后的html
的意思是找到根下面的html节点
(在html
语言中称为标签),再之后的/body
就是获取html节点
下面的 body 节点
,以此类推获取到 span节点
,最通过 /text()
获取span节点
中的内容。
由此可以得到两个知识点:
/ 斜杠符号,表示当前节点的子层级,如例子中的第一个/,就表示要查找根下的子节点。
例子中第二个/,就表示要进入到了html中的层级。
text() 获取当前节点中的内容。如例子中 span/text(),就表示span中的内容。
这样一级一级的向下查找固然很明确,但是很繁琐与复杂,中间的节点是否可以省略?
print(element_obj.xpath('/html/*/*/span/text()'))
print(element_obj.xpath('/html//span/text()'))
print(element_obj.xpath('//span/text()'))
# 以上的结果都是:['电影']
其中:
* 匹配当前层级的任意节点。 如上例中 /html/*,就是匹配html节点下面的任意节点,
本例中html节点下仅有body节点,所有也就匹配到了body节点。
// 当前节点的所有子节点,包含子节点的子节点以及无限层级的子节点。
如例子中,/html// 表示 html节点下的全部子节点。 第三个表示根节点下的全部子节点。
那么,我想要电影名称呢?
print(element_obj.xpath('//div/text()'))
这段代码获取到了包含电影名称的数据,并且还有很多其他内容。因为电影名称所在的节点是 div,但是还有很多节点也是div,这就导致代码把所有的div节点中的内容都获取到。我们可以指定div的属性来剔除其他div标签:
print(element_obj.xpath('//div[@class="title"]/text()'))
节点后面跟一对[]
中括号,中括号内通过@属性名="值"
的语法来指定节点中的属性。
如果我们仅仅要获取第一个电影名称呢?
print(element_obj.xpath('//div[@id="movie1"]/div[1]/text()'))
当当前层级同名节点很多并且我们仅需要其中一个时,可以同样使用[]
中括号,在中括号中填入数字,数字就代表获取第几个节点。(索引值从1开始)。
节点[@属性名] 获取有指定来这个属性的节点
节点[@属性名="值"] 获取指定属性值的节点
节点[从1开始的数字] 获取指定索引的节点
节点[last()] 获取最后一个节点
节点[last()-1] 获取倒数第二个节点
节点[position()<3] 获取前两个节点
上例中,获取一个电影名称是通过id
属性获取的,若不通过id
属性获取则会很冗杂。其实可以通过多次xpath解析操作来使简化操作。
div_element_obj = element_obj.xpath('//div[@class="movie"]')[0]
print(div_element_obj.xpath('./div[1]/text()'))
第一行获取到了class
是movie
的div节点
,因为要获取第一个电影名称,因此获取到索引为0
的节点。
第一行的xpath解析需要从 div节点开始,所以不仅要使用div节点对象,还要在xpath解析中的开头使用./
语法,表示当前,若依旧使用/
或//
语法则还是会从根目录开始。