Python ElementTree解析大XML提取关联数据效率优化咨询
问题背景与需求
我有一个约100MB的XML文件,包含大量与“instrument”相关的数据点,其中重点关注“loan_contract”数据。文件中约有5000个instrument,XML结构示例如下:
<?xml version="1.0" encoding="UTF-8"?> <security_master v="47"> <header> <type>initialize</type> <timestamp>2023-02-08T02:15:12Z</timestamp> </header> <payload> <instrument id="469408967"> <master_information> <instrument_master> </instrument_master> <market_master> </market_master> <organization_master id="321942681"> </organization_master> </master_information> <global_information> </global_information> <debt> <fixed_income> </fixed_income> <bank_loan_details> <amounts> </amounts> <facility> </facility> <loan_contract id="476860356"> </loan_contract> </bank_loan_details> </debt> </instrument> </payload> </security_master>
核心需求
为每个instrument提取所有关联的loan_contract元素,最终生成CSV文件,得到一份包含loan_contract及其关联instrument的唯一列表,忽略无loan_contract的instrument。
注意事项
- 并非所有instrument都关联loan_contract元素(无关联时该标签缺失),部分instrument关联1个,部分关联多个(最大数量未知);
- loan_contract是instrument的曾孙元素;
- loan_contract和instrument的格式分别为
<loan_contract id="___">和<instrument id="___">。
原低效代码
以下代码可实现需求,但运行效率极低:
import xml.etree.ElementTree as ET import pandas as pd #parse XML file tree = ET.parse('/Users/psteward/Documents/py/py/initial.xml') root = tree.getroot() #initialize the list for each field INSTRUMENT = [] LOAN_CONTRACT_ID = [] for item in root.findall('payload/instrument/debt/bank_loan_details/loan_contract'): LOAN_CONTRACT_ID.append(item.get('id')) for contract in LOAN_CONTRACT_ID: INSTRUMENT.append(root.find('payload/instrument/debt/bank_loan_details/loan_contract[@id="%s"]/../../..' % contract).get('id')) # set up dataframe with the lists instruments_df = pd.DataFrame( list( zip( INSTRUMENT, LOAN_CONTRACT_ID ) ), columns = ['INSTRUMENT', 'LOAN_CONTRACT_ID'] ) instruments_df.to_csv("data.csv")
优化建议
1. 层级遍历优化:正向遍历避免反向查找
原代码先收集所有loan_contract,再逐个反向查找对应的instrument,相当于对每个contract重新遍历一次XML,时间复杂度为O(N*M)(N为contract数量,M为instrument数量),效率极低。
改为直接遍历每个instrument节点,在当前节点内查找关联的loan_contract,一次遍历完成数据收集:
import xml.etree.ElementTree as ET import pandas as pd tree = ET.parse('/Users/psteward/Documents/py/py/initial.xml') root = tree.getroot() data = [] # 遍历所有instrument节点 for instrument in root.findall('payload/instrument'): instrument_id = instrument.get('id') # 在当前instrument下查找所有loan_contract contracts = instrument.findall('debt/bank_loan_details/loan_contract') for contract in contracts: contract_id = contract.get('id') data.append([instrument_id, contract_id]) # 生成DataFrame并保存 instruments_df = pd.DataFrame(data, columns=['INSTRUMENT', 'LOAN_CONTRACT_ID']) instruments_df.to_csv("data.csv", index=False)
2. 迭代解析优化内存(可选)
如果XML文件后续进一步增大,使用ET.iterparse()进行迭代解析,无需加载整个文件到内存,能进一步提升内存效率:
import xml.etree.ElementTree as ET import pandas as pd data = [] current_instrument_id = None # 迭代解析XML,只关注instrument和loan_contract节点 for event, elem in ET.iterparse('/Users/psteward/Documents/py/py/initial.xml', events=('start', 'end')): if event == 'start' and elem.tag == 'instrument': current_instrument_id = elem.get('id') elif event == 'end' and elem.tag == 'loan_contract': if current_instrument_id is not None: contract_id = elem.get('id') data.append([current_instrument_id, contract_id]) # 清理已处理的节点,释放内存 elem.clear() instruments_df = pd.DataFrame(data, columns=['INSTRUMENT', 'LOAN_CONTRACT_ID']) instruments_df.to_csv("data.csv", index=False)
3. 细节优化
- 避免使用两个独立列表再
zip,直接用二维列表存储数据,减少内存操作; - 保存CSV时添加
index=False,避免生成多余的索引列。
内容的提问来源于stack exchange,提问作者pdsmth
相关产品推荐
相关产品推荐

