xpath全称XML Path language, 即xml路径语言,最初用来搜寻xml文档的,同样适用于html文档的搜寻
常用规则:
nodename | 选取此节点的所有子节点 |
/ | 从当前节点选取直接子节点 |
// | 从当前节点选取子孙节点 |
. | 选取当前节点 |
.. | 选取当前借点的父节点 |
@ | 选取属性 |
from lxml import etree text = ''' <dd> <i class="board-index board-index-1">1</i> <a href="/films/1203" title="霸王别姬" class="image-link" data-act="boarditem-click" data-val="{movieId:1203}"> <img src="//ms0.meituan.net/mywww/image/loading_2.e3d934bf.png" alt="" class="poster-default" /> <img data-src="http://p1.meituan.net/movie/20803f59291c47e1e116c11963ce019e68711.jpg@160w_220h_1e_1c" alt="霸王别姬" class="board-img" /> </a> <div class="board-item-main"> <div class="board-item-content"> <div class="movie-item-info"> <p class="name"><a href="/films/1203" title="霸王别姬" data-act="boarditem-click" data-val="{movieId:1203}">霸王别姬</a></p> <p class="star"> 主演:张国荣,张丰毅,巩俐 </p> <p class="releasetime">上映时间:1993-01-01(中国香港)</p> </div> <div class="movie-item-number score-num"> <p class="score"><i class="integer">9.</i><i class="fraction">6</i></p> </div> </div> </div> ''' html = etree.HTML(text) result = etree.tostring(html) print(result.decode('utf-8'))
这里首先导入lxml库的etree模块,然后声明了一段html文本,使用html类进行初始化,这样就成功的构造了一个xpath解析对象,需要注意的是,html文本中最后一个节点dd是没有闭合的,但是etree模块自动修正了html文本
调用tostring()方法即可输出修正后的html代码
也可以直接读取文本文件进行解析
html = etree.aprse('./xpath.html',etree.HTMLParser()) result = etree.tostring(html) print(result.decode('utf-8'))
这次得输出结果略有不同,多了一个DOCTYPE的声明,但是对解析结果没有任何影响
所有节点
我们一般使用//开头的xpath规则来选取所有符合要求的节点
html = etree.parse('./xpath.html',etree.HTMLParser()) result = html.xpath('//*') print(result)
使用*号来匹配所有节点,也就是整个html文本中的所有节点都会被获取,返回形式是一个列表,每个元素是Element类型的,其后跟了节点的名称,如html,div,ul等
选取所有的a节点,可以使用//,后面加上节点名称就行
html = etree.parse('./xpath.html',etree.HTMLParser()) result = html.xpath('//a') print(result) #[<Element a at 0x108f47448>, <Element a at 0x108f47488>] print(result[0]) #<Element a at 0x108721488>
可容易看到结果是一个列表形式,每一个元素都是一个Element对象,如果要提取其中一个对象可以直接中括号加索引
属性匹配
//li[@class='item'] #获取所有class为item的li节点
属性获取
我们知道text()获取节点内部文本,那么节点属性该怎样获取呢,其实还是用@符号就可以了
//li/a/@href #查找所有的li节点的直接子节点的href属性值
属性多值匹配
有些时候某些节点的属性可能有多个值
<li class="li li-first" name="item"><a href="link.html">first</a></li>
这里的li节点的class 属性就有两个值li和li-first,如果还用之前的属性匹配获取,就无法匹配了
需要使用contains()函数
//li[contains(@class,'li')]/a/text()
通过contains()方法,第一个参数传入属性名称,第二个参数传入属性值,只要此属性中包含传入的属性值,就可以完成匹配
多属性匹配
还会遇到一种情况,就是根据多个属性确定一个节点,这时就需要匹配多个属性,使用运算符and来连接
//li[contains(@class,"li") and @name="item"]/a/text()
这里的and其实是xpath中的运算符,还有如下
or | 或 |
and | 与 |
mod | 取余 |
| | 返回拥有两边元素节点集 |
+ | |
- | |
* | |
div | 除法 |
= | |
!= |
|
< | |
<= | |
> | |
>= |
按序选择
有时候我们选择属性时,匹配到了多个节点,但是我们只想要其中的某个节点,
//li[1]/a/text() #选取第一个节点,中括号传入1即可,序号是以1开头的 //li[last()]/a/text() #选取最后一个节点 //li[position()<3]/a/text #选取序号小于3的,也就是选择1,2 //li[last()-2]/a/text() #选择倒数第三个节点,因为last()是最后一个,所以last()-2是倒数第三个
节点轴选择
//li[1]/ancestor::* #获取所有祖先节点 //li[1]/ancestor::div #获取指定祖先节点div //li[1]/attribute::* #获取节点的所有属性 //li[1]/child::a[@href="like.html"] #获取所有直接子节点,条件:href属性为like.html的a节点 //li[1]/descendant::span #获取所有子孙节点:条件:返回只包含span节点 //li[1]/following::*[2] #当前节点之后的所有节点,但加了索引匹配,只选择第二个后续节点 //li[1]following-sibling::* #返回节点之后的同级节点