如何使用Python加速提取大型XML文件中的数据?
问题描述
我是Python新手,需要提取XML文件中的属性值。目前使用for循环从xml.dom.minidom.document中获取属性值,但文件包含50万条数据,处理耗时较长。尝试使用lxml时出现错误:module 'lxml' has no attribute 'parse' or 'Xpath',后续调用Xpath时触发AttributeError。
XML格式
<?xml version="1.0" encoding="utf-8"?> <variable_output> <!--version : 1--> <!--object title : Volume (1)--> <!--scalar variable : Temperature (TEMP)--> <POINT> <Vertex> <Position x="-0.176300004" y="-0.103100002" z="-0.153699994"/> <Scalar TEMP="84.192421"/> </Vertex> </POINT> <POINT> <Vertex> <Position x="-0.173557162" y="-0.103100002" z="-0.153699994"/> <Scalar TEMP="83.9050522"/> </Vertex> </POINT> <POINT> <Vertex> <Position x="-0.170814306" y="-0.103100002" z="-0.153699994"/> <Scalar TEMP="83.7506332"/> </Vertex> </POINT> </variable_output>
使用的代码
from xml.dom.minidom import parse import xml.dom.minidom import csv import pandas as pd import numpy as np import os import glob import time from lxml import etree v=[] doc =parse("document.xml") Val = doc.getElementsByTagName("Scalar") t0 = time.time() for s in Val: v=np.append(v,float(s.attributes['TEMP'].value)) res=np.array([v]) t1 = time.time() total = (t1-t0) print('Time for Value', str(total)) # Using lxml doc2=etree.parse("document.xml") # try using Xpath t0 = time.time() temp=doc2.Xpath("/POINT/Vertex/Scaler/@TEMP") t1 = time.time() total2 = t1-t0 print('Time for Value', str(total2)) # save data as csv from xml pd.DataFrame(res.T).to_csv(('Data.csv'),index=False,header=False) #write timestep as csv
报错信息
In [12]: temp=doc2.Xpath("/POINT/Vertex/Scaler/@TEMP") Traceback (most recent call last): File "<ipython-input-12-bbd832a3074e>", line 1, in <module> temp=doc2.Xpath("/POINT/Vertex/Scaler/@TEMP") AttributeError: 'lxml.etree._ElementTree' object has no attribute 'Xpath'
需要解决lxml使用错误,并获得加速提取数据的方法。
解决方案
修复lxml使用错误
你的lxml代码存在三个关键错误:
- 方法名大小写错误:lxml中XPath方法是小写的
xpath(),不是大写的Xpath() - XPath路径错误:XML根节点是
variable_output,路径需要从根开始,即/variable_output/POINT/Vertex/Scalar/@TEMP - 标签名拼写错误:XML中的标签是
<Scalar>,你写成了Scaler
修复后的lxml代码片段:
from lxml import etree import time import numpy as np import pandas as pd t0 = time.time() doc2 = etree.parse("document.xml") # 修正后的XPath表达式和方法调用 temp = doc2.xpath("/variable_output/POINT/Vertex/Scalar/@TEMP") # 转换为float数组 temp_values = np.array(temp, dtype=np.float64) t1 = time.time() print('Time for Value', t1 - t0) # 保存到CSV pd.DataFrame(temp_values).to_csv("Data_lxml.csv", index=False, header=False)
另外,你之前误以为lxml没有parse方法,是因为parse属于lxml.etree模块,你已经正确导入了from lxml import etree,所以etree.parse()是正确用法。
加速数据提取的优化方案
针对50万条数据的大XML文件,推荐以下优化方式:
1. 优化原minidom代码
原代码中循环使用np.append()会频繁重新分配内存,导致效率低下。改为先收集到Python列表,再一次性转换为numpy数组:
from xml.dom.minidom import parse import time import numpy as np t0 = time.time() doc = parse("document.xml") Val = doc.getElementsByTagName("Scalar") # 先收集到列表,避免频繁数组扩容 v = [] for s in Val: v.append(float(s.attributes['TEMP'].value)) res = np.array(v) t1 = time.time() print('Time for Value', t1 - t0)
2. 使用lxml流式解析(iterparse)
对于超大型XML文件,etree.parse()会把整个文档加载到内存,可能导致内存不足。使用etree.iterparse()流式处理,逐节点解析,内存占用更低:
from lxml import etree import time import numpy as np import pandas as pd t0 = time.time() temp_values = [] # 流式解析,只关注Scalar标签 for event, elem in etree.iterparse("document.xml", events=('end',), tag='Scalar'): temp_values.append(float(elem.attrib['TEMP'])) # 清理已处理的节点,释放内存 elem.clear() while elem.getprevious() is not None: del elem.getparent()[0] res = np.array(temp_values) t1 = time.time() print('Time for Value', t1 - t0) pd.DataFrame(res).to_csv("Data_stream.csv", index=False, header=False)
3. 性能对比
- 原minidom方法:最慢,且内存占用高
- lxml xpath方法:比minidom快3-5倍,适合内存足够的场景
- lxml iterparse流式方法:内存占用最低,速度接近xpath方法,适合超大型文件
内容的提问来源于stack exchange,提问作者Sanjeet Limbu
相关产品推荐
相关产品推荐

