lxml是一款高性能的Python XML库,主要用来解析及生成xml和html文件(解析、序列化、转换)。其天生支持Xpath1.0、XSLT1.0、定制元素类,甚至 python 风格的数据绑定接口。lxml基于Cpython实现,其底层是libxml2和libxslt两个C语言库。因此具有较高的性能。
官方文档:https://lxml.de/
在lxml中,lxml.etree模块是最常用的HTML、XML文档解析模块。其中lxml.etree.Element是处理xml的一个核心类,Element对象可以直观的理解为是XML中的节点。使用Element类,可以实现对XML节点、节点属性、节点内文本的操作。
pip install lxml
from lxml import etree
1.创建节点(创建Element对象):
root = etrre.Element('root')
print(root)
2.获取节点名称
print(root.tag)
3.添加子节点
添加子节点的方法有三种:1、直接使用SubElement方法添加子节点。2、创建一个Element对象,使用append的方法将该对象追加到父节点中。3、创建一个Element对象,使用insert方法,将对象添加到父节点指定位置。
child_sub = etree.SubElement(root, 'child_sub')
child_append = etree.Element('child_append')
root.append(child_append)
child_insert = etree.Element('child_insert')
root.insert(0, child_append) # 第一个参数为添加的位置,第二个参数为添加的Element对象
4.删除子节点:
可以使用remove()方法来删除指定子节点。使用clear()方法来清空所有子节点
root.remove(child_sub) # 删除名字为child_sub节点
root.clear() # 清空root的所有子节点
5.访问节点
在Element对象中,访问节点的方法有多种。
(1) 通过列表的方式来访问节点:
child_sub = root[0] # 通过下标来访问子节点
child_sub = root[0: 1][0] # 通过切片的方式来访问节点
for c in root: # 通过遍历来获取所有节点
print(c.tag)
c_append_index = root.index(child_append) # 获取节点的索引
print(len(root)) # 获取子节点的数量
(2) 通过方法来访问节点:
print(child_sub.getparent().tag) # 查询父节点
print(root.getchildren()) # 查询所有子节点
print(root.getroot()) # 获取根节点
print(root.find('b')) # 查询第一个b标签
print(root.findall('.//b')) # 查询所有b标签
在Element中,节点的属性是以字典的形式存储的。
创建属性:
创建属性的方式有两种。1、在创建节点的时候创建属性。2、使用set()方法创建属性。
root = etree.Element('root', language='中文') # 创建节点时创建属性
root.set('hello', 'python') # 使用set方法为root节点添加属性
获取属性:
print(root.get('language')) # 使用get方法获取属性
print(root['language'])
print(root.keys())
print(root.values())
print(root.items())
修改属性:
root['language'] = 'English'
在lxml中访问xml文本的方式有多种,可以使用text、tail属性的方式访问文本。也可以使用xpath语法访问文本。这里只介绍使用text和tail获取和设置文本的属性的方法。xpath后面会具体介绍。
text和tail属性 的区别:
xml中标签一般是成对出现的。但在HTML中则可能会出现单标签,如<html><body>text<br/>tail</body></html>
。
html = etree.Element('html')
body = etree.SubElement(html, 'body')
body.text = 'text' # 给body标签内写入text文本内容
br = etree.SubElement('body', 'br')
br.tail = 'tail' # 在br标签中写入tail文本内容
1. xml文件解析的方法:
xml文件解析的方法有多种,常用的有fromstring、XML、HTML、parse。其中XML和HTML的参数既可以是字符串、也可以是二进制的字节码。
fromstring、XML、parse:返回的是一个Element对象,是一个节点。主要用于解析文档碎片。
parse(): 返回值是一个ElementTree类型的对象,完整的xml树结构。parse主要用来解析完整的文档,而不是Element对象。
参数:
打开的文件或文件类型对象(建议以二进制形式打开
文件名或字符串
HTTP或者FTP的url。
注意:从文件名或者url解析通常比从文件对象解析要快。
xml_data = '<root>data</root>'
# fromstring
root_str = etree.formstring(xml_data)
print(root_str.tag)
# XML
root_xml = etree.XML(xml_data)
print(root_xml.tag)
# HTML,如果没有<html>和<body>标签,则会自动补上
root_html = etree.HTML(xml_data)
print(root_html.tag)
# parse中的参数应该是一个完整的xml或html,同样返回值是一个ElementTree类型的对象,完整的xml树结构。parse主要用来解析完整的文档。
tree =etree.parse('text') #文件解析成元素树
root = tree.getroot() #获取元素树的根节点
print etree.tostring(root, pretty_print=True)
2. xml文件序列化的方法:
我们在生成一个xml文件是有两种方式:1、将Element对象转换成一个xml字符串,然后将其写入到文件中。2、使用ElementTreee对象中的write()方法直接将xml写入文件。
root = '<root>data</root>'
# 将Element对象转换成xml字符串写入文件
root_str = element.tostring(root, pretty_print=True, xml_declartion=True, encoding='utf-8')
with open('text.xml', 'w', encoding='utf-8') as f:
f.write(root_str)
# 将节点(Element对象)转为ElementTree对象。
tree = etree.ElementTree(root)
tree.write('text.xml', pretty_print=True, xml_declartion=True, encoding='utf-8')
参数含义:
补充:ElementTree对象可理解为一个完整的XML树,每个节点都是一个Element对象。而ElementPath则相当于XML中的XPath。用于搜索和定位Element元素。
什么是命名空间?https://www.w3school.com.cn/xml/xml_namespaces.asp
带有命名空间的xml解析处理:
from lxml import etree
str_xml = """
<A xmlns="http://This/is/a/namespace">
<B>dataB1</B>
<B>dataB2</B>
<B><C>datac</C></B>
</A>
"""
xml = etree.fromstring(str_xml) # 解析字符串
ns = xml.nsmap # 获取命名空间
print(ns)
print(ns[None])
>>> {None: 'http://This/is/a/namespace'}
>>> http://This/is/a/namespace
ns = xml.nsmap[None] # 获取命名空间xmlns
# 1. 使用findall方法查找指定节点。
for item in xml.findall(f'{ns}b')
print(item.text)
# 2. 使用xpath语法加命名空间查找指定节点
ns = {'x':root.nsmap[None]} # 获取命名空间
b = root.xpath("//x:B", namespaces=ns)
print(b)
C = root.xapth("//x:B/X:C", namespaces=ns)
print(c)
注意:当xml携带有命名空间(xmlns)的时候,在查找节点时,每一级节点都需要加上命名空间。如果不携带命名空间,是无法查询到该节点的。
除此之外处理命名空间还有一个非常shao的方法,即将所有的命名空间都替换为空,将其当成普通的节点进行处理。
参考:https://blog.csdn.net/rhx_qiuzhi/article/details/105345624
(1)导入lxml 的 etree 库
from lxml import etree
(2)利用etree.HTML,将html字符串(bytes类型或str类型)转化为Element对象,Element对象具有xpath的方法,返回结果的列表
html = etree.HTML(text)
ret_list = html.xpath("xpath语法规则字符串")
(3)xpath方法返回列表的三种情况
from lxml import etree
text = '''
<div>
<ul>
<li class="item-1">
<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) # 也可以使用XML和fromstring方法
# 获取所有的class属性为item-1的href属性
href_list = html.xpath('//li[@class="item-1"]/a/@href')
# 获取所有的class属性为item-1的text内容
text_list = html.xpath('//li[@class="item-1"]/a/text()')
# 组装成字典
for href, title in zip(href_list, title_list):
print({f'{href}': f'{title}'})
注意:lxml.etree.HTML(html_str)方法可以自动补全不完整的标签。
from lxml import etree
# 创建element对象
root = etree.Element('root')
print(root.tag)
# 添加子节点
child_sub = etree.SubElement(root, 'child_sub')
child = etree.Element('child')
child_append = root.append(child) # 通过append向root节点里面追加子节点
child_insert = root.insert(0, child) # 通过insert向root节点开始的位置添加子节点
# 3.删除子节点
# root.remove(child2)
# 4.删除所有子节点
# root.clear()
# 5.以列表的方式操作子节点
print(len(root))
print root.index(child) # 索引号
# 6.生成xml字符串写入xml文件
# 将Element对象转换成xml字符串写入文件
root_str = etree.tostring(root, pretty_print=True, xml_declaration=True, encoding='utf-8')
with open('text.xml', 'wb') as f:
f.write(root_str)
# 将节点(Element对象)转为ElementTree对象。
tree = etree.ElementTree(root)
tree.write('text.xml', pretty_print=True, xml_declartion=True, encoding='utf-8')
参考链接:https://blog.csdn.net/ydw_ydw/article/details/82227699
xpath定位节点以及提取属性或文本内容的语法:
表达式 | 描述 |
---|---|
nodename | 选中该元素。 |
/ | 从根节点选取、或者是元素和元素间的过渡。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点。 |
.. | 选取当前节点的父节点。 |
@ | 选取属性。 |
text() | 选取文本。 |
注意:@符号出现在末尾时,用来提取属性;出现在[]中时是用来匹配属性
选取未知节点的语法:
可以通过通配符来选取未知的html、xml的元素
通配符 | 描述 |
---|---|
* | 匹配任何元素节点。 |
node() | 匹配任何类型的节点。 |
//*
//node()
可以根据标签的属性值、下标等来获取特定的节点
路径表达式 | 结果 |
---|---|
//title[@lang=“eng”] | 选择lang属性值为eng的所有title元素 |
/bookstore/book[1] | 选取属于 bookstore 子元素的第一个 book 元素。 |
/bookstore/book[last()] | 选取属于 bookstore 子元素的最后一个 book 元素。 |
/bookstore/book[last()-1] | 选取属于 bookstore 子元素的倒数第二个 book 元素。 |
/bookstore/book[position()>1] | 选择bookstore下面的book元素,从第二个开始选择 |
//book/title[text()=‘Harry Potter’] | 选择所有book下的title元素,仅仅选择文本为Harry Potter的title元素 |
//book/title[contains(text(), “Harry”)] | 选择所有book下的文本包含Harry的title元素 |
/bookstore/book[price>35.00]/title | 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。 |
关于xpath的下标:
要想利用lxml模块提取数据,需要我们掌握xpath语法规则。接下来我们就来了解一下xpath helper插件,它可以帮助我们练习xpath语法。
xpath helper插件的安装:
1.下载Chrome插件 XPath Helper:
2.把文件的后缀名crx改为rar,然后解压到同名文件夹中
3.把解压后的文件夹拖入到已经开启开发者模式的chrome浏览器扩展程序界面
4.重启浏览器后,访问url之后在页面中点击xpath图标,就可以使用了
5.如果是linux或macOS操作系统,无需操作上述的步骤2,直接将crx文件拖入已经开启开发者模式的chrome浏览器扩展程序界面