XML解析为CSV时identify列重复问题的解决请求
解决XML转CSV中identify列重复及数据匹配问题
看起来你遇到的问题是在生成CSV时,identify列出现了重复值——不过根据你的需求,这个列的值本来就应该是当前XML文件中<Phones>节点下<Get>和<Set>的拼接结果,所以同一个XML的所有行都会用同一个identify值,这其实是符合预期的。不过可能你的代码存在一些其他问题导致了不必要的重复或者不符合预期的输出,我来帮你梳理并修正代码:
原代码存在的问题
- 目录路径设置错误:你把
directory设成了单个XML文件的路径,Path(directory).glob('**/*.xml')会从该文件的父目录开始遍历所有XML,这可能会意外处理多个文件,导致identify值重复出现在不同文件的行中。 - 表头与数据键不匹配:比如表头的
description_num对应XML中<Description>的num属性,但你代码里没有给这个键赋值;还有set_data_xin等表头是小写,但你代码里生成的键是SetData_xin(大写开头),导致这些列数据为空。 - 多余的变量初始化:循环
SetData之后的row = defaultdict(str)是多余的,没有实际作用。
修正后的代码
from xml.etree import ElementTree as ET from collections import defaultdict from pathlib import Path import csv # 修改为XML文件所在的目录路径,而不是单个文件 directory = 'C:/Users/TRY' output_csv = 'try.csv' with open(output_csv, 'w', newline='', encoding='utf-8') as f: writer = csv.writer(f, delimiter=';') headers = [ 'identify', 'id', 'service_code', 'rational', 'qualify', 'description_num', 'description_txt', 'Counter', 'set_data_xin', 'set_data_xax', 'set_data_value', 'set_data_x' ] writer.writerow(headers) # 遍历目录下所有XML文件 for xml_file in Path(directory).glob('**/*.xml'): try: tree = ET.parse(xml_file) root = tree.getroot() # 提取Phones下的Get和Set值,处理可能的空节点 p_get = root.findtext('.//Phones/Get', default='') p_set = root.findtext('.//Phones/Set', default='') identify = f"{p_get}_{p_set}" # 遍历所有START节点 for sn in root.findall('.//START'): row = defaultdict(str) # 处理START的属性,转为小写匹配表头 for k, v in sn.attrib.items(): row[k.lower()] = v # 处理Rational节点 rational_node = sn.find('.//Rational') if rational_node is not None: row['rational'] = rational_node.text # 处理Qualify节点 qualify_node = sn.find('.//Qualify') if qualify_node is not None: row['qualify'] = qualify_node.text # 处理Description节点 desc_node = sn.find('.//Description') if desc_node is not None: row['description_txt'] = desc_node.text row['description_num'] = desc_node.attrib.get('num', '') # 处理SetData节点 for counter, st in enumerate(sn.findall('.//SetData')): # 复制基础row数据,避免不同SetData行互相覆盖 set_data_row = row.copy() # 处理SetData的属性,转为小写匹配表头 for k, v in st.attrib.items(): key = f"set_data_{k.lower()}" # 处理负号和逗号转点 if v.startswith("-"): v = v.lstrip("-").replace(',', '.') set_data_row[key] = v set_data_row['Counter'] = counter # 填充identify值 set_data_row['identify'] = identify # 按表头顺序提取数据并写入 row_data = [set_data_row[header] for header in headers] writer.writerow(row_data) except Exception as e: print(f"处理文件 {xml_file} 时出错: {e}")
关键修正说明
- 路径修正:把
directory改为XML文件所在的目录,确保只处理目标文件,避免意外遍历无关文件。 - 空值安全处理:使用
findtext并设置default参数,避免节点不存在时抛出AttributeError。 - 键名统一匹配:所有数据键转为小写,和表头命名保持一致,确保数据能正确映射到对应的列。
- 数据隔离:处理每个
SetData时复制基础row数据,避免不同SetData行之间的属性互相覆盖。 - 异常捕获:添加异常处理块,方便排查单个文件的解析错误,不会因为一个文件出错导致整个程序终止。
内容的提问来源于stack exchange,提问作者E199504
相关产品推荐
相关产品推荐

