python解析库lxml的简单使用
lxml是python的一个解析库,支持HTML和XML的解析,支持XPath解析方式,而且解析效率非常高
XPath,全称XML Path Language,即XML路径语言,它是一门在XML文档中查找信息的语言,它最初是用来搜寻XML文档的,但是它同样适用于HTML文档的搜索
XPath的选择功能十分强大,它提供了非常简明的路径选择表达式,另外,它还提供了超过100个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等,几乎所有我们想要定位的节点,都可以用XPath来选择
XPath于1999年11月16日成为W3C标准,它被设计为供XSLT、XPointer以及其他XML解析软件使用,更多的文档可以访问其官方网站:https://www.w3.org/TR/xpath/
1、python库lxml的安装
windows系统下的安装:
#pip安装 pip3 install lxml #wheel安装 #下载对应系统版本的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml pip3 install lxml-4.2.1-cp36-cp36m-win_amd64.whl
linux下安装:
yum install -y epel-release libxslt-devel libxml2-devel openssl-devel
pip3 install lxml
验证安装:
$python3 >>>import lxml
2、XPath常用规则
表达式 | 描述 |
nodename | 选取此节点的所有子节点 |
/ | 从当前节点选取直接子节点 |
// | 从当前节点选取子孙节点 |
. | 选取当前节点 |
.. | 选取当前节点的父节点 |
@ | 选取属性 |
* | 通配符,选择所有元素节点与元素名 |
@* | 选取所有属性 |
[@attrib] | 选取具有给定属性的所有元素 |
[@attrib='value'] | 选取给定属性具有给定值的所有元素 |
[tag] | 选取所有具有指定元素的直接子节点 |
[tag='text'] | 选取所有具有指定元素并且文本内容是text节点 |
(1)读取文本解析节点
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)读取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)获取所有节点
返回一个列表每个元素都是Element类型,所有节点都包含在其中
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>]
如要获取li节点,可以使用//后面加上节点名称,然后调用xpath()方法
html.xpath('//li') #获取所有子孙节点的li节点
(4)获取子节点
通过/或者//即可查找元素的子节点或者子孙节点,如果想选择li节点的所有直接a节点,可以这样使用
result=html.xpath('//li/a') #通过追加/a选择所有li节点的所有直接a节点,因为//li用于选中所有li节点,/a用于选中li节点的所有直接子节点a
(5)获取父节点
我们知道通过连续的/或者//可以查找子节点或子孙节点,那么要查找父节点可以使用..来实现也可以使用parent::来获取父节点
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']
(6)属性匹配
在选取的时候,我们还可以用@
符号进行属性过滤。比如,这里如果要选取class
为item-1
的li
节点,可以这样实现:
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)
(7)文本获取
我们用XPath中的text()方法获取节点中的文本
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)
(8)属性获取
使用@符号即可获取节点的属性,如下:获取所有li节点下所有a节点的href属性
result=html.xpath('//li/a/@href') #获取a的href属性 result=html.xpath('//li//@href') #获取所有li子孙节点的href属性
(9)属性多值匹配
如果某个属性的值有多个时,我们可以使用contains()函数来获取
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()就能精确匹配到节点了 [] ['第一个']
(10)多属性匹配
另外我们还可能遇到一种情况,那就是根据多个属性确定一个节点,这时就需要同时匹配多个属性,此时可用运用and运算符来连接使用:
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']
(11)XPath中的运算符
运算符 | 描述 | 实例 | 返回值 |
or |
或 | age=19 or age=20 | 如果age等于19或者等于20则返回true反正返回false |
and | 与 | age>19 and age<21 | 如果age等于20则返回true,否则返回false |
mod | 取余 | 5 mod 2 | 1 |
| | 取两个节点的集合 | //book | //cd | 返回所有拥有book和cd元素的节点集合 |
+ | 加 | 6+4 | 10 |
- | 减 | 6-4 | 2 |
* | 乘 | 6*4 | 24 |
div | 除法 | 8 div 4 | 2 |
= | 等于 | age=19 | true |
!= | 不等于 | age!=19 | true |
< | 小于 | age<19 | true |
<= | 小于或等于 | age<=19 | true |
> | 大于 | age>19 | true |
>= | 大于或等于 | age>=19 | true |
此表参考来源:http://www.w3school.com.cn/xpath/xpath_operators.asp
(12)按序选择
有时候,我们在选择的时候某些属性可能同时匹配多个节点,但我们只想要其中的某个节点,如第二个节点或者最后一个节点,这时可以利用中括号引入索引的方法获取特定次序的节点:
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) # ['第一个', '第二个', '第三个', '第四个'] ['第一个'] ['第四个'] ['第三个'] ['第二个']
这里使用了last()、position()函数,在XPath中,提供了100多个函数,包括存取、数值、字符串、逻辑、节点、序列等处理功能,它们的具体作用可参考:http://www.w3school.com.cn/xpath/xpath_functions.asp
(13)节点轴选择
XPath提供了很多节点选择方法,包括获取子元素、兄弟元素、父元素、祖先元素等,示例如下:
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>]
以上使用的是XPath轴的用法,更多轴的用法可参考:http://www.w3school.com.cn/xpath/xpath_axes.asp
(14)案例应用:抓取TIOBE指数前20名排行开发语言
#!/usr/bin/env python #coding:utf-8 import requests from requests.exceptions import RequestException from lxml import etree from lxml.etree import ParseError import json def one_to_page(html): headers={ 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36' } try: response=requests.get(html,headers=headers) body=response.text #获取网页内容 except RequestException as e: print('request is error!',e) try: html=etree.HTML(body,etree.HTMLParser()) #解析HTML文本内容 result=html.xpath('//table[contains(@class,"table-top20")]/tbody/tr//text()') #获取列表数据 pos = 0 for i in range(20): if i == 0: yield result[i:5] else: yield result[pos:pos+5] #返回排名生成器数据 pos+=5 except ParseError as e: print(e.position) def write_file(data): #将数据重新组合成字典写入文件并输出 for i in data: sul={ '2018年6月排行':i[0], '2017年6排行':i[1], '开发语言':i[2], '评级':i[3], '变化率':i[4] } with open('test.txt','a',encoding='utf-8') as f: f.write(json.dumps(sul,ensure_ascii=False) + '\n') #必须格式化数据 f.close() print(sul) return None def main(): url='https://www.tiobe.com/tiobe-index/' data=one_to_page(url) revaule=write_file(data) if revaule == None: print('ok') if __name__ == '__main__': main() # {'2018年6月排行': '1', '2017年6排行': '1', '开发语言': 'Java', '评级': '15.368%', '变化率': '+0.88%'} {'2018年6月排行': '2', '2017年6排行': '2', '开发语言': 'C', '评级': '14.936%', '变化率': '+8.09%'} {'2018年6月排行': '3', '2017年6排行': '3', '开发语言': 'C++', '评级': '8.337%', '变化率': '+2.61%'} {'2018年6月排行': '4', '2017年6排行': '4', '开发语言': 'Python', '评级': '5.761%', '变化率': '+1.43%'} {'2018年6月排行': '5', '2017年6排行': '5', '开发语言': 'C#', '评级': '4.314%', '变化率': '+0.78%'} {'2018年6月排行': '6', '2017年6排行': '6', '开发语言': 'Visual Basic .NET', '评级': '3.762%', '变化率': '+0.65%'} {'2018年6月排行': '7', '2017年6排行': '8', '开发语言': 'PHP', '评级': '2.881%', '变化率': '+0.11%'} {'2018年6月排行': '8', '2017年6排行': '7', '开发语言': 'JavaScript', '评级': '2.495%', '变化率': '-0.53%'} {'2018年6月排行': '9', '2017年6排行': '-', '开发语言': 'SQL', '评级': '2.339%', '变化率': '+2.34%'} {'2018年6月排行': '10', '2017年6排行': '14', '开发语言': 'R', '评级': '1.452%', '变化率': '-0.70%'} {'2018年6月排行': '11', '2017年6排行': '11', '开发语言': 'Ruby', '评级': '1.253%', '变化率': '-0.97%'} {'2018年6月排行': '12', '2017年6排行': '18', '开发语言': 'Objective-C', '评级': '1.181%', '变化率': '-0.78%'} {'2018年6月排行': '13', '2017年6排行': '16', '开发语言': 'Visual Basic', '评级': '1.154%', '变化率': '-0.86%'} {'2018年6月排行': '14', '2017年6排行': '9', '开发语言': 'Perl', '评级': '1.147%', '变化率': '-1.16%'} {'2018年6月排行': '15', '2017年6排行': '12', '开发语言': 'Swift', '评级': '1.145%', '变化率': '-1.06%'} {'2018年6月排行': '16', '2017年6排行': '10', '开发语言': 'Assembly language', '评级': '0.915%', '变化率': '-1.34%'} {'2018年6月排行': '17', '2017年6排行': '17', '开发语言': 'MATLAB', '评级': '0.894%', '变化率': '-1.10%'} {'2018年6月排行': '18', '2017年6排行': '15', '开发语言': 'Go', '评级': '0.879%', '变化率': '-1.17%'} {'2018年6月排行': '19', '2017年6排行': '13', '开发语言': 'Delphi/Object Pascal', '评级': '0.875%', '变化率': '-1.28%'} {'2018年6月排行': '20', '2017年6排行': '20', '开发语言': 'PL/SQL', '评级': '0.848%', '变化率': '-0.72%'}
XPath的更多用法参考:http://www.w3school.com.cn/xpath/index.asp
python lxml库的更多用法参考:http://lxml.de/
出处:https://www.cnblogs.com/zhangxinqi/p/9210211.html
==========================================================================================
Python-- lxml用法
目录
lxml库(lxml安装可查看上一篇文章)
- python的HTML/XML的解析器
- 官方文档: http://lxml.de/index.html
- 功能:
- 解析HTML
- 文件读取
- etree和XPath的配合使用
围绕三个问题:
问题1:有一个XML文件,如何解析
问题2:解析后,如果查找、定位某个标签
问题3:定位后如何操作标签,比如访问属性、文本内容等
导入模块,该库常用的XML处理功能都在lxml.etree中
from lxml import etree
Element类
Element是XML处理的核心类,Element对象可以直观的理解为XML的节点,大部分XML节点的处理都是围绕该类进行的。这部分包括三个内容:节点的操作、节点属性的操作、节点内文本的操作。
1、节点操作
1、创建Element对象
使用Element
方法,参数即节点名称。
-
'root')root = etree.Element(
-
print(root)
-
<Element root at 0x2da0708>
2、获取节点名称
使用tag
属性,获取节点的名称。
-
print(root.tag)
-
root
3、输出XML内容
使用tostring
方法输出XML内容,参数为Element对象。
-
print(etree.tostring(root))
-
b'<root><child1/><child2/><child3/></root>'
4、添加子节点
使用SubElement
方法创建子节点,第一个参数为父节点(Element对象),第二个参数为子节点名称。
-
'child1')child1 = etree.SubElement(root,
-
'child2')child2 = etree.SubElement(root,
-
'child3')child3 = etree.SubElement(root,
5、删除子节点
使用remove
方法删除指定节点,参数为Element对象。clear方法清空所有节点。
-
# 删除指定子节点root.remove(child1)
-
print(etree.tostring(root))
-
b'<root><child2/><child3/></root>'
-
# 清除所有子节点root.clear()
-
print(etree.tostring(root))
-
b'<root/>'
6、以列表的方式操作子节点
可以将Element对象的子节点视为列表进行各种操作:
-
0] # 下标访问child = root[
-
print(child.tag)
-
child1
-
-
# 子节点数量print(len(root))
-
3
-
-
# 获取索引号root.index(child2)
-
1
-
-
for child in root: # 遍历
-
print(child.tag)
-
child1
-
child2
-
child3
-
-
0, etree.Element('child0')) # 插入root.insert(
-
1] # 切片start = root[:
-
-1:]end = root[
-
-
0].tag)print(start[
-
child0
-
0].tag)print(end[
-
child3
-
-
'child4') ) # 尾部添加root.append( etree.Element(
-
print(etree.tostring(root))
-
b'<root><child0/><child1/><child2/><child3/><child4/></root>'
7、获取父节点
使用getparent
方法可以获取父节点。
-
print(child1.getparent().tag)
-
root
2、属性操作
属性是以key-value的方式存储的,就像字典一样。
1、创建属性
可以在创建Element对象时同步创建属性,第二个参数即为属性名和属性值:
-
'root', interesting='totally')root = etree.Element(
-
print(etree.tostring(root))
-
b'<root interesting="totally"/>'
-
也可以使用set方法给已有的Element对象添加属性,两个参数分别为属性名和属性值:
-
-
'hello', 'Huhu')root.set(
-
print(etree.tostring(root))
-
b'<root interesting="totally" hello="Huhu"/>'
2、获取属性
属性是以key-value的方式存储的,就像字典一样。直接看例子
-
# get方法获得某一个属性值
-
'interesting'))print(root.get(
-
totally
-
-
# keys方法获取所有的属性名
-
sorted(root.keys())
-
['hello', 'interesting']
-
-
# items方法获取所有的键值对
-
for name, value in sorted(root.items()):
-
'%s = %r' % (name, value))print(
-
hello = 'Huhu'
-
interesting = 'totally'
也可以用attrib属性一次拿到所有的属性及属性值存于字典中:
-
attributes = root.attrib
-
print(attributes)
-
{'interesting': 'totally', 'hello': 'Huhu'}
-
-
'good'] = 'Bye' # 字典的修改影响节点attributes[
-
'good'))print(root.get(
-
Bye
3、文本操作
标签及标签的属性操作介绍完了,最后就剩下标签内的文本了。可以使用text和tail属性、或XPath的方式来访问文本内容。
1、text和tail属性
一般情况,可以用Element的text属性访问标签的文本。
-
'root')root = etree.Element(
-
'Hello, World!'root.text =
-
print(root.text)
-
Hello, World!
-
print(etree.tostring(root))
-
b'<root>Hello, World!</root>'```
-
-
XML的标签一般是成对出现的,有开有关,但像HTML则可能出现单一的标签,如下面这段代码中的`<br/>`
-
-
`<html><body>Text<br/>Tail</body></html>`
-
-
Element类提供了tail属性支持单一标签的文本获取。
-
```python
-
'html')html = etree.Element(
-
'body')body = etree.SubElement(html,
-
'Text'body.text =
-
print(etree.tostring(html))
-
b'<html><body>Text</body></html>'
-
-
'br')br = etree.SubElement(body,
-
print(etree.tostring(html))
-
b'<html><body>Text<br/></body></html>'
-
-
# tail仅在该标签后面追加文本
-
'Tail'br.tail =
-
print(etree.tostring(br))
-
b'<br/>Tail'
-
-
print(etree.tostring(html))
-
b'<html><body>Text<br/>Tail</body></html>'
-
-
# tostring方法增加method参数,过滤单一标签,输出全部文本
-
'text'))print(etree.tostring(html, method=
-
b'TextTail'
2、XPath方式
-
# 方式一:过滤单一标签,返回文本
-
'string()'))print(html.xpath(
-
TextTail
-
-
# 方式二:返回列表,以单一标签为分隔
-
'//text()'))print(html.xpath(
-
['Text', 'Tail']
方法二获得的列表,每个元素都会带上它所属节点及文本类型信息,如下:
-
'//text()'))texts = html.xpath(
-
-
0])print(texts[
-
Text
-
# 所属节点
-
0].getparent()parent = texts[
-
print(parent.tag)
-
body
-
-
1], texts[1].getparent().tag)print(texts[
-
Tail br
-
-
# 文本类型:是普通文本还是tail文本
-
0].is_text)print(texts[
-
True
-
1].is_text)print(texts[
-
False
-
1].is_tail)print(texts[
-
True
4、文件解析与输出
这部分讲述如何将XML文件解析为Element对象,以及如何将Element对象输出为XML文件。
1. 文件解析
文件解析常用的有fromstring、XML和HTML三个方法。接受的参数都是字符串。
-
'<root>data</root>'xml_data =
-
-
# fromstring方法
-
root1 = etree.fromstring(xml_data)
-
print(root1.tag)
-
root
-
print(etree.tostring(root1))
-
b'<root>data</root>'
-
-
# XML方法,与fromstring方法基本一样
-
root2 = etree.XML(xml_data)
-
print(root2.tag)
-
root
-
print(etree.tostring(root2))
-
b'<root>data</root>'
-
-
# HTML方法,如果没有<html>和<body>标签,会自动补上
-
root3 = etree.HTML(xml_data)
-
print(root3.tag)
-
html
-
print(etree.tostring(root3))
-
b'<html><body><root>data</root></body></html>'
2. 输出
输出其实就是前面一直在用的tostring方法了,这里补充xml_declaration和encoding两个参数,前者是XML声明,后者是指定编码。
-
'<root><a><b/></a></root>')root = etree.XML(
-
-
print(etree.tostring(root))
-
b'<root><a><b/></a></root>'
-
-
# XML声明
-
True))print(etree.tostring(root, xml_declaration=
-
b"<?xml version='1.0' encoding='ASCII'?>\n<root><a><b/></a></root>"
-
-
# 指定编码
-
'iso-8859-1'))print(etree.tostring(root, encoding=
-
b"<?xml version='1.0' encoding='iso-8859-1'?>\n<root><a><b/></a></root>"
5、ElementPath
讲ElementPath前,需要引入ElementTree类,一个ElementTree对象可理解为一个完整的XML树,每个节点都是一个Element对象。而ElementPath则相当于XML中的XPath。用于搜索和定位Element元素。
这里介绍两个常用方法,可以满足大部分搜索、查询需求,它们的参数都是XPath语句(关于XPath的学习可以查看我之前的一片文章):
findall():返回所有匹配的元素,返回列表
find():返回匹配到的第一个元素
-
"<root><a x='123'>aText<b/><c/><b/></a></root>")root = etree.XML(
-
-
# 查找第一个b标签
-
'b'))print(root.find(
-
None
-
'a').tag)print(root.find(
-
a
-
-
# 查找所有b标签,返回Element对象组成的列表
-
for b in root.findall('.//b') ][ b.tag
-
['b', 'b']
-
-
# 根据属性查询
-
'.//a[@x]')[0].tag)print(root.findall(
-
a
-
'.//a[@y]'))print(root.findall(
-
[]
以上内容大多来自此原文:Python lxml教程-SKYue
6、案例(尤其最后的一篇代码)
- 解析HTML,案例1.py
- 文件读取,案例2.html, 案例2.py
- etree和XPath的配合使用, 案例3.py
案例1.py
-
'''
-
安装lxml
-
'''
-
from lxml import etree
-
-
'''
-
用lxml来解析HTML代码
-
'''
-
-
text = '''
-
<div>
-
<ul>
-
<li class="item-0"> <a href="0.html"> first item </a></li>
-
<li class="item-1"> <a href="1.html"> first item </a></li>
-
<li class="item-2"> <a href="2.html"> first item </a></li>
-
<li class="item-3"> <a href="3.html"> first item </a></li>
-
<li class="item-4"> <a href="4.html"> first item </a></li>
-
<li class="item-5"> <a href="5.html"> first item </a>
-
</ul>
-
</div>
-
'''
-
-
# 利用etree.HTML把字符串解析成HTML文档
-
html = etree.HTML(text)
-
s = etree.tostring(html)
-
print(s)
案例2.html
-
"1.0" encoding="utf-8" xml version=
-
-
<bookstore>
-
<book category="cooking">
-
<title lang="en">Everyday Italian</title>
-
<author>Gidada De</author>
-
<year>2018</year>
-
<price>23</price>
-
</book>
-
-
<book category="education">
-
<title lang="en">Python is Python</title>
-
<author>Food War</author>
-
<year>2008</year>
-
<price>83</price>
-
</book>
-
-
<book category="sport">
-
<title lang="en">Running</title>
-
<author>Klaus Kuka</author>
-
<year>2010</year>
-
<price>43</price>
-
</book>
-
-
</bookstore>
案例2.py
-
from lxml import etree
-
-
# 只能读取xml格式内容,html报错
-
html = etree.parse("./v30.html")
-
-
rst = etree.tostring(html, pretty_print=True)
-
print(rst)
案例3.py
-
from lxml import etree
-
-
# 只能读取xml格式内容,html报错
-
html = etree.parse("./v30.html")
-
print(type(html))
-
-
rst = html.xpath('//book')
-
print(type(rst))
-
print(rst)
-
-
# xpath的意识是,查找带有category属性值为sport的book元素
-
rst = html.xpath('//book[@category="sport"]')
-
print(type(rst))
-
print(rst)
-
-
# xpath的意识是,查找带有category属性值为sport的book元素下的year元素
-
rst = html.xpath('//book[@category="sport"]/year')
-
rst = rst[0]
-
print(type(rst))
-
print(rst.tag)
-
print(rst.text)
目前有很多xml,html文档的parser,如标准库的xml.etree , beautifulsoup , 还有lxml. 都用下来感觉lxml不错,速度也还行,就他了.
围绕三个问题:
- 问题1:有一个XML文件,如何解析
- 问题2:解析后,如果查找、定位某个标签
- 问题3:定位后如何操作标签,比如访问属性、文本内容等
这些操作应该算是比较基础的,还可以自己在网上查找相关教程,官网更详细一点,进阶xpath语法,要在以后操作xml文件和html文件用上.
-
#!/usr/bin/python
-
# coding=utf-8
-
# __author__='dahu'
-
#
-
'''
-
Element是XML处理的核心类,
-
Element对象可以直观的理解为XML的节点,大部分XML节点的处理都是围绕该类进行的。
-
这部分包括三个内容:节点的操作、节点属性的操作、节点内文本的操作。
-
'''
-
from lxml import etree
-
-
# 1.创建element
-
root = etree.Element('root')
-
print root, root.tag
-
-
# 2.添加子节点
-
child1 = etree.SubElement(root, 'child1')
-
child2 = etree.SubElement(root, 'child2')
-
-
# 3.删除子节点
-
# root.remove(child2)
-
-
# 4.删除所有子节点
-
# root.clear()
-
-
# 5.以列表的方式操作子节点
-
print(len(root))
-
print root.index(child1) # 索引号
-
root.insert(0, etree.Element('child3')) # 按位置插入
-
root.append(etree.Element('child4')) # 尾部添加
-
-
# 6.获取父节点
-
print(child1.getparent().tag)
-
# print root[0].getparent().tag #用列表获取子节点,再获取父节点
-
'''以上都是节点操作'''
-
-
# 7.创建属性
-
# root.set('hello', 'dahu') #set(属性名,属性值)
-
# root.set('hi', 'qing')
-
-
# 8.获取属性
-
# print(root.get('hello')) #get方法
-
# print root.keys(),root.values(),root.items() #参考字典的操作
-
# print root.attrib #直接拿到属性存放的字典,节点的attrib,就是该节点的属性
-
'''以上是属性的操作'''
-
-
# 9.text和tail属性
-
# root.text = 'Hello, World!'
-
# print root.text
-
-
# 10.test,tail和text的结合
-
html = etree.Element('html')
-
html.text = 'html.text'
-
body = etree.SubElement(html, 'body')
-
body.text = 'wo ai ni'
-
child = etree.SubElement(body, 'child')
-
child.text='child.text' #一般情况下,如果一个节点的text没有内容,就只有</>符号,如果有内容,才会<>,</>都有
-
child.tail = 'tails' # tail是在标签后面追加文本
-
print(etree.tostring(html))
-
# print(etree.tostring(html, method='text')) # 只输出text和tail这种文本文档,输出的内容连在一起,不实用
-
-
#11.Xpath方式
-
# print(html.xpath('string()')) #这个和上面的方法一样,只返回文本的text和tail
-
print(html.xpath('//text()')) #这个比较好,按各个文本值存放在列表里面
-
tt=html.xpath('//text()')
-
print tt[0].getparent().tag #这个可以,首先我可以找到存放每个节点的text的列表,然后我再根据text找相应的节点
-
# for i in tt:
-
# print i,i.getparent().tag,'\t',
-
-
#12.判断文本类型
-
print tt[0].is_text,tt[-1].is_tail #判断是普通text文本,还是tail文本
-
'''以上都是文本的操作'''
-
-
#13.字符串解析,fromstring方式
-
xml_data = '<html>html.text<body>wo ai ni<child>child.text</child>tails</body></html>'
-
root1=etree.fromstring(xml_data) #fromstring,字面意思,直接来源字符串
-
# print root1.tag
-
# print etree.tostring(root1)
-
-
#14.xml方式
-
root2 = etree.XML(xml_data) #和fromstring基本一样,
-
print etree.tostring(root2)
-
-
#15.文件类型解析
-
tree =etree.parse('text') #文件解析成元素树
-
root3 = tree.getroot() #获取元素树的根节点
-
print etree.tostring(root3,pretty_print=True)
-
-
parser= etree.XMLParser(remove_blank_text=True) #去除xml文件里的空行
-
root = etree.XML("<root> <a/> <b> </b> </root>",parser)
-
print etree.tostring(root)
-
-
#16.html方式
-
xml_data1='<root>data</root>'
-
root4 = etree.HTML(xml_data1)
-
print(etree.tostring(root4))#HTML方法,如果没有<html>和<body>标签,会自动补上
-
#注意,如果是需要补全的html格式:这样处理哦
-
with open("quotes-1.html",'r')as f:
-
a=H.document_fromstring(f.read().decode("utf-8"))
-
-
for i in a.xpath('//div[@class="quote"]/span[@class="text"]/text()'):
-
print i
-
-
#17.输出内容,输出xml格式
-
print etree.tostring(root)
-
print(etree.tostring(root, xml_declaration=True,pretty_print=True,encoding='utf-8'))#指定xml声明和编码
-
'''以上是文件IO操作'''
-
-
#18.findall方法
-
root = etree.XML("<root><a x='123'>aText<b/><c/><b/></a></root>")
-
print(root.findall('a')[0].text)#findall操作返回列表
-
print(root.find('.//a').text) #find操作就相当与找到了这个元素节点,返回匹配到的第一个元素
-
print(root.find('a').text)
-
print [ b.text for b in root.findall('.//a') ] #配合列表解析,相当帅气!
-
print(root.findall('.//a[@x]')[0].tag) #根据属性查询
-
'''以上是搜索和定位操作'''
-
print(etree.iselement(root))
-
print root[0] is root[1].getprevious() #子节点之间的顺序
-
print root[1] is root[0].getnext()
-
'''其他技能'''
-
# 遍历元素数
-
root = etree.Element("root")
-
etree.SubElement(root, "child").text = "Child 1"
-
etree.SubElement(root, "child").text = "Child 2"
-
etree.SubElement(root, "another").text = "Child 3"
-
etree.SubElement(root[0], "childson").text = "son 1"
-
# for i in root.iter(): #深度遍历
-
# for i in root.iter('child'): #只迭代目标值
-
# print i.tag,i.text
-
# print etree.tostring(root,pretty_print=True)
关注我】。(●'◡'●)
如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的【因为,我的写作热情也离不开您的肯定与支持,感谢您的阅读,我是【Jack_孟】!
本文来自博客园,作者:jack_Meng,转载请注明原文链接:https://www.cnblogs.com/mq0036/p/13161350.html
【免责声明】本文来自源于网络,如涉及版权或侵权问题,请及时联系我们,我们将第一时间删除或更改!
posted on 2020-06-19 09:28 jack_Meng 阅读(16093) 评论(0) 编辑 收藏 举报