解析库xpath使用方法
XPath,全称XML Path Language,即XML路径语言,它是一门在XML文档中查找信息的语言。XPath 最初设计是用来搜寻XML文档的,但是它同样适用于HTML文档的搜索。
XPath的选择功能十分强大,它提供了非常简洁明了的路径选择表达式,另外它还提供了超过100个内建函数用于字符串、数值、时间的匹配以及节点、序列的处理等等,几乎所有我们想要定位的节点都可以用XPath来选择。
1.常用规则
表达式 | 解释 |
---|---|
nodename | 选取此节点的所有子节点 |
/ | 从当前节点选取直接子节点 |
// | 从当前节点选取子孙节点 |
. | 选取当前节点 |
… | 选取当前节点的父节点 |
@ | 选取属性 |
* | 匹配所有节点 |
2.补全html标签
etree.tostring(ele)补全缺失的标签,ele必须是element_or_tree,不可直接传入html文本。
etree.tostring()可以将一个元素对象转换为一个字符串,比如你获取到了某个元素对象,你想看一看元素对象里面是什么,就可以使用这个方法。
from lxml import etree
html = '''
<div>
<ul>
<li class="item-0"><a href="https://ask.hellobi.com/link1.html">first item</a></li>
<li class="item-1"><a href="https://ask.hellobi.com/link2.html">second item</a></li>
<li class="item-inactive"><a href="https://ask.hellobi.com/link3.html">third item</a></li>
<li class="item-1"><a href="https://ask.hellobi.com/link4.html">fourth item</a></li>
<li class="item-0"><a href="https://ask.hellobi.com/link5.html">fifth item</a>
</ul>
</div>
'''
ele = etree.HTML(html)
# tostring补全html中缺的标签,返回二进制数据
result = etree.tostring(ele)
print(result.decode('utf-8'))
我们来看下结果,可以看到经过处理之后li节点标签被补全,并且还自动添加了body、html节点。
<html><body><div>
<ul>
<li class="item-0"><a href="https://ask.hellobi.com/link1.html">first item</a></li>
<li class="item-1"><a href="https://ask.hellobi.com/link2.html">second item</a></li>
<li class="item-inactive"><a href="https://ask.hellobi.com/link3.html">third item</a></li>
<li class="item-1"><a href="https://ask.hellobi.com/link4.html">fourth item</a></li>
<li class="item-0"><a href="https://ask.hellobi.com/link5.html">fifth item</a>
</li></ul>
</div>
</body></html>
3.读取html文件进行解析
etree.parse()函数读取html文件,对其内容进行解析。
from lxml import etree
ele = etree.parse('./test.html', etree.HTMLParser())
result = etree.tostring(ele, encoding='utf-8')
print(result.decode('utf-8'))
test.html文件内容同上,看一看结果,发现多了一行DOCTYPE,对解析结果是不影响的。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><div>
<ul>
<li class="item-0"><a href="https://ask.hellobi.com/link1.html">first item</a></li>
<li class="item-1"><a href="https://ask.hellobi.com/link2.html">second item</a></li>
<li class="item-inactive"><a href="https://ask.hellobi.com/link3.html">third item</a></li>
<li class="item-1"><a href="https://ask.hellobi.com/link4.html">fourth item</a></li>
<li class="item-0"><a href="https://ask.hellobi.com/link5.html">fifth item</a>
</li></ul>
</div></body></html>
4.匹配某节点下所有符合条件的元素.//
我们先来对比一下:
/ 表示从当前节点匹配所有的子节点
// 表示从当前节点匹配所有的子孙节点
//* 表示从任意节点匹配所有的子孙节点
. 表示选取当前节点
.// 则是表示从当前节点下匹配所有该节点的子孙节点
5.同时匹配多个xpath表达式
这个符号用于在一个xpath中写多个表达式用,用 | 分开,每个表达式互不干扰。
print(ele.xpath('//ul//a/text() | //li/@class'))
6.child:选取当前节点的子元素
print(ele.xpath('//ul/child::*')) # 获取ul所有的子元素
print(ele.xpath('//ul/child::li')) # 获取ul所有子元素中的li
7.parent:选取当前节点的父节点
print(ele.xpath('//li/parent::*')) # 获取li所有的父元素
print(ele.xpath('//li/parent::ul')) # 获取li所有父元素中的ul
8.attribute:选取当前节点的属性
print(ele.xpath('//ul/li/attribute::*')) # 获取li的所有属性
print(ele.xpath('//ul/li/attribute::class')) # 获取li所有属性中的class属性
9.ancestor:父辈元素 / ancestor-or-self:父辈元素及当前元素
print(ele.xpath('//div[@id="testid"]/ancestor::*')) # 获取div所有父元素
print(ele.xpath('//div[@id="testid"]/ancestor::div')) # 获取div所有父元素中的div
print(ele.xpath('//div[@id="testid"]/ancestor-or-self::*')) # 获取div所有父元素及当前元素
print(ele.xpath('//div[@id="testid"]/ancestor-or-self::div')) # 获取div所有父元素及当前元素中的div
10.descendant:后代 / descendant-or-self:后代及当前节点本身
方法同上
11.following :选取当前节点的结束标签之后的所有节点,包括节点的子孙节点
print(ele.xpath('//li[@class="item-0"]/following::li')) # 获取li后面的所有节点中的li
for e in ele.xpath('//li[@class="item-0"]/following::li'):
print(etree.tostring(e)) # 将元素对象转换为字符串,方便查看获取到了什么
12.preceding:选取当前节点的开始标签之前的所有节点,包括节点的子孙节点
print(ele.xpath('//li[@class="item-0"]/preceding::li')) # 获取li前面的所有节点中的li
for e in ele.xpath('//li[@class="item-0"]/preceding::li'):
print(etree.tostring(e)) # 将元素对象转换为字符串,方便查看获取到了什么
13.following-sibling:选取当前节点之后的所有同级节点
注意跟following的区别是:following-sibling只能匹配同级节点
print(ele.xpath('//li[@class="item-0"]/following-sibling::li')) # 获取li后面所有同级节点中的li
14.preceding-sibling:选取当前节点之前的所有同级节点
注意跟preceding的区别是:preceding-sibling只能匹配同级节点
print(ele.xpath('//li[@class="item-0"]/preceding-sibling::li')) # 获取li前面所有同级节点中的li
15.position:定位
选择偶数项:[position()=(position() mod 2 = 0)]
选择奇数项:[position()=(position() mod 2 != 0)]
ele.xpath('//li[1]') # 选择第一个li
ele.xpath('//li[position()<3]') # 选择定位小于3的li
ele.xpath('//li[position()<6 and position()>3]') # 选择定位大于3小于6的li
ele.xpath('//li[last()]') # 选择最后一个li
ele.xpath('//li[last()-2]') # 选择倒数第三个li
# 重点:匹配奇数项和偶数项
ele.xpath('//tr/td[position()=(position() mod 2 = 0)]') # 匹配tr下面所有的偶数个td
ele.xpath('//tr/td[position()=(position() mod 2 != 0)]') # 匹配tr下面所有的奇数个td
16.contains:包含
选择文本中或属性中包含某些内容的元素
ele.xpath('//*[contains(text(),"123")]') # 选择所有文本中包含123的节点
ele.xpath('//*[contains(@href, "test")]') # 选择所有属性href中包含test的节点
17.starts-with:以…开始
选择文本中或属性中以某些内容开始的元素
ele.xpath('//li[starts-with(@class,"test")]') # 匹配类名以test开头的li
18.count:统计节点下的某个子节点数量
选择当前节点下某个子节点数量为多少的元素
ele.xpath('//ul[count(li)>10]') # 获取子节点li数量超过10个的ul
19.not:非
选择没有某个属性值的元素
ele.xpath('//li[not(@data)]') # 获取所有没有data属性的li
20.多条件匹配or/and
当你的干扰因素较多时,你需要多条件精确匹配想要的元素
ele.xpath('//li[@data="24" or @code="55"]') # 匹配满足至少一个条件的节点
ele.xpath('//li[@data="24"]/text() | //li[@code="55"]/text()') # 或者是匹配多个条件
ele.xpath('//li[@data="24" and @code="55"]') # 匹配同时满足条件的节点
21.获取文本
/text() 获取当前节点文本,不包括子孙节点;
//text() 获取当前节点和子孙节点的文本,若有多个,返回多个字符串的列表;
string() 获取匹配第一个节点的所有文本(包括子孙节点)
ele.xpath('//li[@class="item-0"]/a/text()')
ele.xpath('//li[@class="item-0"]//text()')
ele.xpath('string(//li[@class="item-0"])')