xpath解析库详解
简单说明
XPath的选择功能十分强大,它提供了非常简洁明了的路径选择表达式。XPath相对于正则表达式显得更加简洁明了,对于网页的节点来说,它可以定义id、class或其他属性。而且节点之间还有层次关系,在网页中可以通过XPath来定位一个或多个节点
规则
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点 |
/ | 从当前结点选取直接子节点 |
// | 从当前节点选取子孙结点 |
. | 选取当前节点 |
… | 选取当前节点的父节点 |
@ | 选取属性 |
具体的代码展示
一.解析文本代码
from lxml import etree
text = '''
<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>
</ul>
</div>
'''
html = etree.HTML(text)
result = etree.tostring(html)
print(result.decode('utf-8'))
这里首先导入lxml库的etree模块,然后声明一段HTML文本,调用HTML类进行初始化,这样就成功构造了一个XPath解析对象。这里需要注意的是,HTML文本中的最后一个li节点是没有闭合的,但是etree模块可以自动修正HTML文本。
这里我们调用tostring()方法即可输出修正后的HTML代码,但是结果是bytes类型。这里利用decode()方法将其转成str类型,结果如下:
<html><body><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></ul>
</div>
</body></html>
二.解析文本文件
from lxml import etree
html=etree.parse('***/test.html',etree.HTMLParser())
result=etree.tostring(html)
print(result.decode('utf-8'))
三.获取所有节点
from lxml import etree
text = '''
<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>
</ul>
</div>
'''
html = etree.HTML(text)
result=html.xpath('//li')
print(result)
print(result[0])
四.属性匹配
from lxml import etree
html=etree.parse('***/test.html',etree.HTMLParser())
result=html.xpath('//li[@class="item-0"]')
print(result)
五.文本获取
我们使用XPath中的text()方法获取节点中的文本
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//li[@class="item-0"]/text()')
print(result)
# 运行结果如下:
# ['\n ']
我们并没有获取到任何文本,只获取到了一个换行符,原因在于XPath中text()前面是/,而此处的/的含义是选取直接子节点,很明显li的直接子节点都是a节点,文本都在a节点的内部,所有这里匹配到的结果就是被修正的li节点内部的换行符,因为自动修正的li节点的尾标签换行了,因此,如果想获取li节点内部的文本,就有两种方式,一种是先选取a节点再获取文本,另一种就是使用//。接下来,我们看一下二者的区别。
from lxml import etree
html=etree.parse('***/test.html',etree.HTMLParser())
result=html.xpath('//li[@class="item-0"]/a/text()')
print(result)
# 运行结果如下:
# ['first item', 'fifth item']
from lxml import etree
html=etree.parse('***/test.html',etree.HTMLParser())
result=html.xpath('//li[@class="item-0"]//text()')
print(result)
# 运行结果如下:
# ['first item', 'fifth item', '\n']
六.属性获取
from lxml import etree
html=etree.parse('***/test.html',etree.HTMLParser())
result=html.xpath('//li/a/@href')
print(result)
这里我们通过@href既可获取节点的href属性。注意,此处和属性匹配的方法不同,属性匹配是中括号加属性名和值来限定某个属性,如[@href=“link1.html”],而此处的@href指的是获取节点的某个属性,二者需要做好区分
七.属性多值匹配
from lxml import etree
text = '''
<li class="li li-first"><a href="link.html">first item</a></li>
'''
html = etree.HTML(text)
result = html.xpath('//li[@class="li"]/a/text()')
print(result)
# 这里HTML文本中li节点的class属性有两个值li和li-first,此时如果还想用之前的属性匹配获取,就无法匹配了,此时运行结果如下:
# []
需要用contains()函数
from lxml import etree
text = '''
<li class="li li-first"><a href="link.html">first item</a></li>
'''
html = etree.HTML(text)
result = html.xpath('//li[contains(@class,"li")]/a/text()')
print(result)
# 这样通过contains()方法,第一个参数传入属性名称,第二个参数传入属性值,只要此属性包含所传入的属性值,就可以完成匹配。
# 运行结果如下:
# ['first item']
八.多属性匹配
我们可能会遇到多个属性确定一个节点,这时就需要同时匹配多个属性。此时,可以使用运算符and来连接
from lxml import etree
text = '''
<li class="li li-first" name="item"><a href="link.html">first item</a></li>
'''
html = etree.HTML(text)
result = html.xpath('//li[contains(@class, "li") and @name="item"]/a/text()')
print(result)
# 运行结果如下:
# ['first item']
九.按序选择
from lxml import etree
text = '''
<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>
</ul>
</div>
'''
html = etree.HTML(text)
result = html.xpath('//li[1]/a/text()')
print(result)
result = html.xpath('//li[last()]/a/text()')
print(result)
result = html.xpath('//li[position()<3]/a/text()')
print(result)
result = html.xpath('//li[last()-2]/a/text()')
print(result)
# 第一次选择时,我们选取了第一个li节点,中括号中传入数字1即可。注意,这里和代码中不同,序号是以1开头的,不是以0开头
# 第二次选择时,我们选取了最后一个li节点,中括号传入last()即可,返回便是最后一个li节点
# 第三次选择时,我们选取了位置小于3的li节点,也就是位置序号为1和2的节点,得到的结果就是前两个li节点。
# 第四次选择时,我们选取了倒数第三个li节点,中括号传入last()-2即可。因为last是最后一个,所有last()-2就是倒是第三个。
# 运行结果如下:
# ['first item']
# ['fifth item']
# ['first item', 'second item']
# ['third item']
这里使用了last(),position()函数,在XPath中还有许多类似的函数,具体可参考
http://www.w3school.com.cn/xpath/xpath_functions.asp