第86集:XML文件操作
学习目标
- 理解XML数据格式的基本概念
- 掌握xml.etree.ElementTree模块的使用方法
- 学会读取和解析XML文件
- 掌握XML数据的创建和修改
- 学会处理XML文件中的常见问题
一、XML概述
1.1 什么是XML
XML(eXtensible Markup Language,可扩展标记语言)是一种用于存储和传输数据的标记语言。它是一种自描述的格式,可以定义自己的标签。
1.2 XML的特点
- 可扩展性:可以自定义标签
- 结构化:使用树形结构组织数据
- 跨平台:独立于操作系统和编程语言
- 可读性:人类可读,易于理解和调试
- 标准化:W3C标准,广泛应用
1.3 XML与JSON的对比
| 特性 | XML | JSON |
|---|---|---|
| 可读性 | 较好 | 更好 |
| 数据量 | 较大 | 较小 |
| 解析速度 | 较慢 | 较快 |
| 注释支持 | 支持 | 不支持 |
| 属性支持 | 支持 | 不支持 |
| 命名空间 | 支持 | 不支持 |
1.4 XML基本结构
<?xml version="1.0" encoding="UTF-8"?>
<root>
<student id="1">
<name>张三</name>
<age>25</age>
<city>北京</city>
</student>
<student id="2">
<name>李四</name>
<age>30</age>
<city>上海</city>
</student>
</root>二、XML基本概念
2.1 元素(Element)
元素是XML的基本构建块,由开始标签、内容和结束标签组成。
<name>张三</name>2.2 属性(Attribute)
属性提供元素的额外信息,位于开始标签中。
<student id="1">2.3 文本内容(Text Content)
元素之间的文本内容。
<name>张三</name>2.4 根元素(Root Element)
XML文档的顶层元素,只能有一个。
<root>
<!-- 其他元素 -->
</root>2.5 嵌套元素(Nested Elements)
元素可以包含其他元素。
<student>
<name>张三</name>
<age>25</age>
</student>三、xml.etree.ElementTree模块
3.1 导入模块
import xml.etree.ElementTree as ET3.2 主要类和函数
- Element:表示XML元素
- ElementTree:表示整个XML文档树
- **parse()**:解析XML文件
- **fromstring()**:从字符串解析XML
- **tostring()**:将元素转换为字符串
四、解析XML文件
4.1 使用parse()解析XML文件
import xml.etree.ElementTree as ET
tree = ET.parse('data.xml')
root = tree.getroot()4.2 使用fromstring()解析XML字符串
import xml.etree.ElementTree as ET
xml_str = '<root><name>张三</name></root>'
root = ET.fromstring(xml_str)4.3 遍历XML元素
import xml.etree.ElementTree as ET
tree = ET.parse('data.xml')
root = tree.getroot()
# 遍历所有子元素
for child in root:
print(child.tag, child.attrib)五、访问XML数据
5.1 访问元素标签
import xml.etree.ElementTree as ET
root = ET.fromstring('<root><name>张三</name></root>')
print(root.tag) # 输出:root5.2 访问元素属性
import xml.etree.ElementTree as ET
root = ET.fromstring('<root id="1"><name>张三</name></root>')
print(root.attrib) # 输出:{'id': '1'}
print(root.get('id')) # 输出:15.3 访问元素文本内容
import xml.etree.ElementTree as ET
root = ET.fromstring('<root><name>张三</name></root>')
name = root.find('name')
print(name.text) # 输出:张三5.4 访问子元素
import xml.etree.ElementTree as ET
root = ET.fromstring('<root><name>张三</name><age>25</age></root>')
# 使用find()查找第一个匹配的子元素
name = root.find('name')
print(name.text)
# 使用findall()查找所有匹配的子元素
children = root.findall('*')
for child in children:
print(child.tag, child.text)六、创建XML文件
6.1 创建元素
import xml.etree.ElementTree as ET
root = ET.Element('root')
name = ET.SubElement(root, 'name')
name.text = '张三'6.2 添加属性
import xml.etree.ElementTree as ET
root = ET.Element('root')
root.set('id', '1')6.3 添加子元素
import xml.etree.ElementTree as ET
root = ET.Element('root')
student = ET.SubElement(root, 'student')
name = ET.SubElement(student, 'name')
name.text = '张三'6.4 保存XML文件
import xml.etree.ElementTree as ET
root = ET.Element('root')
name = ET.SubElement(root, 'name')
name.text = '张三'
tree = ET.ElementTree(root)
tree.write('output.xml', encoding='utf-8', xml_declaration=True)七、修改XML文件
7.1 修改元素文本
import xml.etree.ElementTree as ET
tree = ET.parse('data.xml')
root = tree.getroot()
name = root.find('name')
name.text = '李四'
tree.write('data.xml', encoding='utf-8')7.2 修改元素属性
import xml.etree.ElementTree as ET
tree = ET.parse('data.xml')
root = tree.getroot()
root.set('id', '2')
tree.write('data.xml', encoding='utf-8')7.3 添加新元素
import xml.etree.ElementTree as ET
tree = ET.parse('data.xml')
root = tree.getroot()
age = ET.SubElement(root, 'age')
age.text = '25'
tree.write('data.xml', encoding='utf-8')7.4 删除元素
import xml.etree.ElementTree as ET
tree = ET.parse('data.xml')
root = tree.getroot()
name = root.find('name')
root.remove(name)
tree.write('data.xml', encoding='utf-8')八、XML数据查询
8.1 使用find()查询
import xml.etree.ElementTree as ET
root = ET.fromstring('''
<root>
<student id="1">
<name>张三</name>
</student>
<student id="2">
<name>李四</name>
</student>
</root>
''')
# 查找第一个student元素
student = root.find('student')
print(student.get('id'))8.2 使用findall()查询
import xml.etree.ElementTree as ET
root = ET.fromstring('''
<root>
<student id="1">
<name>张三</name>
</student>
<student id="2">
<name>李四</name>
</student>
</root>
''')
# 查找所有student元素
students = root.findall('student')
for student in students:
print(student.get('id'))8.3 使用XPath查询
import xml.etree.ElementTree as ET
root = ET.fromstring('''
<root>
<student id="1">
<name>张三</name>
<age>25</age>
</student>
<student id="2">
<name>李四</name>
<age>30</age>
</student>
</root>
''')
# 查询所有name元素
names = root.findall('.//name')
for name in names:
print(name.text)
# 查询id为1的student
student = root.find(".//student[@id='1']")
print(student.find('name').text)九、XML数据处理
9.1 遍历XML树
import xml.etree.ElementTree as ET
def traverse_xml(element, level=0):
indent = ' ' * level
print(f"{indent}{element.tag}: {element.text}")
for child in element:
traverse_xml(child, level + 1)
root = ET.fromstring('<root><name>张三</name><age>25</age></root>')
traverse_xml(root)9.2 XML转换为字典
import xml.etree.ElementTree as ET
def xml_to_dict(element):
result = {}
for child in element:
if len(child) > 0:
result[child.tag] = xml_to_dict(child)
else:
result[child.tag] = child.text
return result
root = ET.fromstring('<root><name>张三</name><age>25</age></root>')
data = xml_to_dict(root)
print(data)9.3 字典转换为XML
import xml.etree.ElementTree as ET
def dict_to_xml(data, root_tag='root'):
root = ET.Element(root_tag)
for key, value in data.items():
child = ET.SubElement(root, key)
if isinstance(value, dict):
child.extend(dict_to_xml(value, key))
else:
child.text = str(value)
return root
data = {'name': '张三', 'age': '25'}
root = dict_to_xml(data)
print(ET.tostring(root, encoding='unicode'))十、XML文件常见问题
10.1 编码问题
import xml.etree.ElementTree as ET
# 写入XML文件时指定编码
root = ET.Element('root')
tree = ET.ElementTree(root)
tree.write('data.xml', encoding='utf-8', xml_declaration=True)
# 读取XML文件时指定编码
tree = ET.parse('data.xml')
root = tree.getroot()10.2 处理命名空间
import xml.etree.ElementTree as ET
xml_str = '''
<root xmlns="http://example.com">
<name>张三</name>
</root>
'''
root = ET.fromstring(xml_str)
namespace = {'ns': 'http://example.com'}
name = root.find('ns:name', namespace)
print(name.text)10.3 处理CDATA
import xml.etree.ElementTree as ET
# ElementTree不直接支持CDATA,需要使用其他方法
# 可以使用xml.dom.minidom模块处理CDATA
from xml.dom import minidom
doc = minidom.Document()
root = doc.createElement('root')
doc.appendChild(root)
cdata = doc.createCDATASection('<special>data</special>')
name = doc.createElement('name')
name.appendChild(cdata)
root.appendChild(name)
print(doc.toxml())10.4 处理XML注释
import xml.etree.ElementTree as ET
# ElementTree不直接支持注释
# 可以使用xml.dom.minidom模块处理注释
from xml.dom import minidom
doc = minidom.Document()
root = doc.createElement('root')
doc.appendChild(root)
comment = doc.createComment('这是一个注释')
root.appendChild(comment)
name = doc.createElement('name')
name.appendChild(doc.createTextNode('张三'))
root.appendChild(name)
print(doc.toxml())十一、XML高级操作
11.1 美化XML输出
import xml.etree.ElementTree as ET
from xml.dom import minidom
def prettify(element):
rough_string = ET.tostring(element, 'utf-8')
reparsed = minidom.parseString(rough_string)
return reparsed.toprettyxml(indent=" ")
root = ET.Element('root')
name = ET.SubElement(root, 'name')
name.text = '张三'
print(prettify(root))11.2 XML验证
import xml.etree.ElementTree as ET
def validate_xml(xml_str):
try:
ET.fromstring(xml_str)
return True, "XML格式正确"
except ET.ParseError as e:
return False, f"XML格式错误:{e}"
# 有效的XML
valid_xml = '<root><name>张三</name></root>'
is_valid, message = validate_xml(valid_xml)
print(f"是否有效:{is_valid}, 消息:{message}")
# 无效的XML
invalid_xml = '<root><name>张三</name>'
is_valid, message = validate_xml(invalid_xml)
print(f"是否有效:{is_valid}, 消息:{message}")11.3 合并XML文件
import xml.etree.ElementTree as ET
def merge_xml(file1, file2, output):
tree1 = ET.parse(file1)
tree2 = ET.parse(file2)
root1 = tree1.getroot()
root2 = tree2.getroot()
for child in root2:
root1.append(child)
tree1.write(output, encoding='utf-8', xml_declaration=True)11.4 XML数据转换
import xml.etree.ElementTree as ET
def transform_xml(input_file, output_file, transform_func):
tree = ET.parse(input_file)
root = tree.getroot()
transform_func(root)
tree.write(output_file, encoding='utf-8', xml_declaration=True)
# 使用示例
def add_id_attribute(root):
for i, child in enumerate(root, 1):
child.set('id', str(i))十二、最佳实践
12.1 使用with语句
虽然ElementTree不支持with语句,但可以手动确保文件关闭。
import xml.etree.ElementTree as ET
tree = ET.parse('data.xml')
root = tree.getroot()
# 处理数据
tree.write('output.xml', encoding='utf-8')12.2 指定编码方式
# 推荐
tree.write('data.xml', encoding='utf-8', xml_declaration=True)
# 不推荐
tree.write('data.xml')12.3 异常处理
import xml.etree.ElementTree as ET
try:
tree = ET.parse('data.xml')
root = tree.getroot()
except FileNotFoundError:
print('文件不存在')
except ET.ParseError:
print('XML格式错误')
except Exception as e:
print(f'发生错误:{e}')12.4 使用XPath查询
import xml.etree.ElementTree as ET
root = ET.fromstring(xml_str)
# 推荐:使用XPath
students = root.findall('.//student')
# 不推荐:手动遍历
for child in root:
if child.tag == 'student':
process(child)十三、总结
本集学习了XML文件操作的知识:
- XML数据格式的基本概念和结构
- xml.etree.ElementTree模块的使用方法
- XML文件的读取和解析
- XML数据的创建和修改
- XML数据的查询和处理
- XML文件常见问题和最佳实践
XML是一种重要的数据交换格式,在配置文件、Web服务、数据存储等场景中广泛应用。下一集我们将学习Excel文件操作。
十四、练习题
- 创建一个XML文件,包含学生信息(姓名、年龄、成绩、城市),然后读取并显示。
- 编写一个函数,将XML元素转换为字典。
- 实现一个函数,查询特定属性值的元素。
- 编写一个程序,合并两个XML文件的数据。
- 实现一个函数,美化XML输出。
- 编写一个函数,验证XML文件的有效性。
- 实现一个程序,统计XML文件中特定元素的数量。
- 编写一个程序,将JSON文件转换为XML文件。