lxml模块学习
一、简介
lxml是python的一个解析库,支持HTML和XML的解析,支持XPath解析方式,而且解析效率非常高。
XPath的选择功能十分强大,它提供了非常简明的路径选择表达式,另外,它还提供了超过100个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等,几乎所有我们想要定位的节点,都可以用XPath来选择。
二、运用
xpath 的思想是通过 路径表达 去寻找节点。节点包括元素
,属性
,和内容。
表达式 | 描述 |
nodename | 选取此节点的所有子节点 |
/ | 从当前节点选取直接子节点 |
// | 从当前节点选取子孙节点 |
. | 选取当前节点 |
.. | 选取当前节点的父节点 |
@ | 选取属性 |
* | 通配符,选择所有元素节点与元素名 |
@* | 选取所有属性 |
[@attrib] | 选取具有给定属性的所有元素 |
[@attrib='value'] | 选取给定属性具有给定值的所有元素 |
[tag] | 选取所有具有指定元素的直接子节点 |
1、lxml 读取文本解析节点
from lxml import etree text=''' <div> <ul> <li class="item-0"><a href="link1.html">第一个</a></li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-0"><a href="link5.html">a属性</a> </ul> </div> ''' html=etree.HTML(text) #初始化生成一个XPath解析对象 result=etree.tostring(html,encoding='utf-8') #解析对象输出代码 print(type(html)) print(type(result)) print(result.decode('utf-8')) #etree会修复HTML文本节点 <class 'lxml.etree._Element'> <class 'bytes'> <html><body><div> <ul> <li class="item-0"><a href="link1.html">第一个</a></li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-0"><a href="link5.html">a属性</a> </li></ul> </div> </body></html>
2、lxml 读取HTML文件进行解析
from lxml import etree html=etree.parse('test.html',etree.HTMLParser()) #指定解析器HTMLParser会根据文件修复HTML文件中缺失的如声明信息 result=etree.tostring(html) #解析成字节 #result=etree.tostringlist(html) #解析成列表 print(type(html)) print(type(result)) print(result) #输出 <class 'lxml.etree._ElementTree'> <class 'bytes'> b'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">\n<html><body><div> \n <ul> \n <li class="item-0"><a href="link1.html">first item</a></li> \n <li class="item-1"><a href="link2.html">second item</a></li> \n <li class="item-inactive"><a href="link3.html">third item</a></li> \n <li class="item-1"><a href="link4.html">fourth item</a></li> \n <li class="item-0"><a href="link5.html">fifth item</a> \n </li></ul> \n </div> \n</body></html>'
3、xpath运用
(1)获取所有节点
from lxml import etree html=etree.parse('test',etree.HTMLParser()) result=html.xpath('//*') #//代表获取子孙节点,*代表获取所有 print(type(html)) print(type(result)) print(result) # <class 'lxml.etree._ElementTree'> <class 'list'> [<Element html at 0x754b210048>, <Element body at 0x754b210108>, <Element div at 0x754b210148>, <Element ul at 0x754b210188>, <Element li at 0x754b2101c8>, <Element a at 0x754b210248>, <Element li at 0x754b210288>, <Element a at 0x754b2102c8>, <Element li at 0x754b210308>, <Element a at 0x754b210208>, <Element li at 0x754b210348>, <Element a at 0x754b210388>, <Element li at 0x754b2103c8>, <Element a at 0x754b210408>]
(2)获取父节点
from lxml import etree from lxml.etree import HTMLParser text=''' <div> <ul> <li class="item-0"><a href="link1.html">第一个</a></li> <li class="item-1"><a href="link2.html">second item</a></li> </ul> </div> ''' html=etree.HTML(text,etree.HTMLParser()) result=html.xpath('//a[@href="link2.html"]/../@class') result1=html.xpath('//a[@href="link2.html"]/parent::*/@class') print(result) print(result1) # ['item-1'] ['item-1']
(3)属性匹配
from lxml import etree from lxml.etree import HTMLParser text=''' <div> <ul> <li class="item-0"><a href="link1.html">第一个</a></li> <li class="item-1"><a href="link2.html">second item</a></li> </ul> </div> ''' html=etree.HTML(text,etree.HTMLParser()) result=html.xpath('//li[@class="item-1"]') print(result)
(4)文本获取
from lxml import etree text=''' <div> <ul> <li class="item-0"><a href="link1.html">第一个</a></li> <li class="item-1"><a href="link2.html">second item</a></li> </ul> </div> ''' html=etree.HTML(text,etree.HTMLParser()) result=html.xpath('//li[@class="item-1"]/a/text()') #获取a节点下的内容 result1=html.xpath('//li[@class="item-1"]//text()') #获取li下所有子孙节点的内容 print(result) print(result1)
(5)属性多值匹配
from lxml import etree text1=''' <div> <ul> <li class="aaa item-0"><a href="link1.html">第一个</a></li> <li class="bbb item-1"><a href="link2.html">second item</a></li> </ul> </div> ''' html=etree.HTML(text1,etree.HTMLParser()) result=html.xpath('//li[@class="aaa"]/a/text()') result1=html.xpath('//li[contains(@class,"aaa")]/a/text()') print(result) print(result1) #通过第一种方法没有取到值,通过contains()就能精确匹配到节点了 [] ['第一个']
(6)多属性匹配
from lxml import etree text1=''' <div> <ul> <li class="aaa" name="item"><a href="link1.html">第一个</a></li> <li class="aaa" name="fore"><a href="link2.html">second item</a></li> </ul> </div> ''' html=etree.HTML(text1,etree.HTMLParser()) result=html.xpath('//li[@class="aaa" and @name="fore"]/a/text()') result1=html.xpath('//li[contains(@class,"aaa") and @name="fore"]/a/text()') print(result) print(result1) # ['second item'] ['second item']
(7)运算符
运算符 | 描述 | 实例 | 返回值 |
or | 或 | age=10 or age=20 | 如果age等于10或者等于20则返回true反正返回false |
and | 与 | age>19 and age<21 | 如果age等于20则返回true,否则返回false |
mod | 取余 | 5 mod 2 | 1 |
| | 取两个节点的集合 | //book | //cd | 返回所有拥有book和cd元素的节点集合 |
+ | 加 | 5+4 | 9 |
- | 减 | 5-4 | 1 |
* | 乘 | 5*4 | 20 |
div | 除法 | 6 div 3 | 2 |
= | 等于 | age=10 | true |
!= | 不等于 | age!=10 | true |
< | 小于 | age<10 | true |
<= | 小于或等于 | age<=10 | true |
> | 大于 | age>10 | true |
>= | 大于或等于 | age>=10 | true |
(8)按序选择
from lxml import etree text1=''' <div> <ul> <li class="aaa" name="item"><a href="link1.html">第一个</a></li> <li class="aaa" name="item"><a href="link1.html">第二个</a></li> <li class="aaa" name="item"><a href="link1.html">第三个</a></li> <li class="aaa" name="item"><a href="link1.html">第四个</a></li> </ul> </div> ''' html=etree.HTML(text1,etree.HTMLParser()) result=html.xpath('//li[contains(@class,"aaa")]/a/text()') #获取所有li节点下a节点的内容 result1=html.xpath('//li[1][contains(@class,"aaa")]/a/text()') #获取第一个 result2=html.xpath('//li[last()][contains(@class,"aaa")]/a/text()') #获取最后一个 result3=html.xpath('//li[position()>2 and position()<4][contains(@class,"aaa")]/a/text()') #获取第一个 result4=html.xpath('//li[last()-2][contains(@class,"aaa")]/a/text()') #获取倒数第三个 print(result) print(result1) print(result2) print(result3) print(result4) # ['第一个', '第二个', '第三个', '第四个'] ['第一个'] ['第四个'] ['第三个'] ['第二个']
(9)节点轴选择
from lxml import etree text1=''' <div> <ul> <li class="aaa" name="item"><a href="link1.html">第一个</a></li> <li class="aaa" name="item"><a href="link1.html">第二个</a></li> <li class="aaa" name="item"><a href="link1.html">第三个</a></li> <li class="aaa" name="item"><a href="link1.html">第四个</a></li> </ul> </div> ''' html=etree.HTML(text1,etree.HTMLParser()) result=html.xpath('//li[1]/ancestor::*') #获取所有祖先节点 result1=html.xpath('//li[1]/ancestor::div') #获取div祖先节点 result2=html.xpath('//li[1]/attribute::*') #获取所有属性值 result3=html.xpath('//li[1]/child::*') #获取所有直接子节点 result4=html.xpath('//li[1]/descendant::a') #获取所有子孙节点的a节点 result5=html.xpath('//li[1]/following::*') #获取当前子节之后的所有节点 result6=html.xpath('//li[1]/following-sibling::*') #获取当前节点的所有同级节点 # [<Element html at 0x3ca6b960c8>, <Element body at 0x3ca6b96088>, <Element div at 0x3ca6b96188>, <Element ul at 0x3ca6b961c8>] [<Element div at 0x3ca6b96188>] ['aaa', 'item'] [<Element a at 0x3ca6b96248>] [<Element a at 0x3ca6b96248>] [<Element li at 0x3ca6b96308>, <Element a at 0x3ca6b96348>, <Element li at 0x3ca6b96388>, <Element a at 0x3ca6b963c8>, <Element li at 0x3ca6b96408>, <Element a at 0x3ca6b96488>] [<Element li at 0x3ca6b96308>, <Element li at 0x3ca6b96388>, <Element li at 0x3ca6b96408>]
(10)自定义函数
from lxml import etree #定义函数 def ends_with(context,s1,s2): return s1[0].endswith(s2) if __name__ == '__main__': doc=''' <div> <ul class='ul items'> <li class="item-0 active"><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> ''' html = etree.XML(doc) ns = etree.FunctionNamespace(None) ns['ends-with'] = ends_with #将ends_with方法注册到方法命名空间中 print(html.xpath("//li[ends-with(@class,'active')]")) print(html.xpath("//li[ends-with(@class,'active')]/a/text()"))
- 形参
s1
会传入xpath中的第一个参数@class
,但这里注意@class是个列表 - 形参
s2
会传入xpath中的第二个参数'active'
,'active'
是个字符串
4、xpath支持的内建函数
- number last() 谓语中返回在兄弟元素中排在最末尾的
- number position() 谓语中,返回该元素在兄弟中的排名
- number count(node-set) 返回子节点的个数 ,
node-set
代表子节点的表达式 - node-set id("foo") 返回唯一id为foo的元素 。尝试了多次,未尝试出来,这里的唯一id是基于xml的,有下面这样一句话。所以确实没测试出来
-
string local-name(node-set?)
-
string name(node-set?)
-
string namespace-uri(node-set?)
-
string concat(string, string, string*)
-
boolean starts-with(string, string)
-
boolean contains(string, string)
-
string substring-before(string, string)
-
string substring-after(string, string)
-
string substring(string, number, number?)
-
number string-length(string?)
-
string normalize-space(string?)
-
number sum(node-set)
-
number floor(number)
-
number ceiling(number)
-
number round(number)
5、xpath使用工具
chome生成xpath表达式
经常使用chome的小伙伴的都应该知道这个功能,在 审查
状态下(快捷键ctrl+shift+i,F12),定位到元素(快捷键ctrl+shift+c) ,在Elements选项卡中,右键元素 Copy->Copy xpath,就能得到该元素的xpath了
6、xpath插件
为chome装上XPath Helper就可以很轻松的检验自己的xpath是否正确了。安装插件需要kxsw(使用lanternFQ,或者Astar VPN),安装好插件后,在chrome右上角点插件的图标,调出插件的黑色界面,编辑好xpath表达式,表达式选中的元素被标记为黄色