如何解决Python提取XML同类型多实例数据导出Excel的覆盖问题
问题
现有Python脚本可提取MODS/METS格式XML数据并导出到Excel,已实现单组XML对应Excel一行,但无法处理同一类型的多实例数据——比如XML中存在多个<mods:titleInfo type="alternative">节点时,仅最后一个实例被记录。需要将这类多实例数据合并到同一单元格(用分隔符区分)或分栏展示,且不能新增Excel行。
现有代码片段
import xml.etree.ElementTree as ET import openpyxl def extract_mods_data(xml_path): tree = ET.parse(xml_path) root = tree.getroot() ns = {'mods': 'http://www.loc.gov/mods/v3'} data = {} # 当前仅提取最后一个alternative title alt_titles = root.findall('.//mods:titleInfo[@type="alternative"]/mods:title', ns) if alt_titles: data['alternative_title'] = alt_titles[-1].text # 其他字段提取逻辑... return data def export_to_excel(data_list, excel_path): wb = openpyxl.Workbook() ws = wb.active headers = ['alternative_title', ...] ws.append(headers) for data in data_list: row = [data.get(header, '') for header in headers] ws.append(row) wb.save(excel_path) # 调用示例 xml_files = ['sample.xml'] data_list = [extract_mods_data(file) for file in xml_files] export_to_excel(data_list, 'output.xlsx')
XML示例片段
<mods:mods xmlns:mods="http://www.loc.gov/mods/v3"> <mods:titleInfo type="alternative"> <mods:title>替代标题1</mods:title> </mods:titleInfo> <mods:titleInfo type="alternative"> <mods:title>替代标题2</mods:title> </mods:titleInfo> <!-- 其他节点 --> </mods:mods>
解决方案
核心思路是将同一类型的多实例节点内容批量收集,再通过合并字符串或分栏填充的方式存入Excel行中,以下是两种可行方案:
方案1:合并到同一单元格(推荐)
将所有同类型节点的文本用分隔符(如分号、换行符)拼接,存入单个单元格:
def extract_mods_data(xml_path): tree = ET.parse(xml_path) root = tree.getroot() ns = {'mods': 'http://www.loc.gov/mods/v3'} data = {} # 收集所有alternative title并拼接 alt_title_elements = root.findall('.//mods:titleInfo[@type="alternative"]/mods:title', ns) if alt_title_elements: # 用分号+空格分隔,也可替换为'\n'实现单元格内换行 data['alternative_title'] = '; '.join([elem.text.strip() for elem in alt_title_elements if elem.text]) else: data['alternative_title'] = '' # 其他多实例字段同理处理,比如多个创作者 creators = root.findall('.//mods:name[@type="personal"]/mods:namePart', ns) if creators: data['creators'] = '; '.join([c.text.strip() for c in creators if c.text]) return data
方案2:分栏展示
若需要每个实例单独占用一列,先遍历所有XML文件确定该字段的最大实例数,再生成对应表头并填充数据:
def get_max_instance_count(xml_files, ns, xpath): # 遍历所有文件,获取指定节点的最大实例数 max_count = 0 for file in xml_files: tree = ET.parse(file) root = tree.getroot() count = len(root.findall(xpath, ns)) if count > max_count: max_count = count return max_count # 初始化配置 xml_files = ['sample.xml'] ns = {'mods': 'http://www.loc.gov/mods/v3'} max_alt_title_count = get_max_instance_count(xml_files, ns, './/mods:titleInfo[@type="alternative"]/mods:title') headers = [f'alternative_title_{i+1}' for i in range(max_alt_title_count)] # 追加其他字段表头... def extract_mods_data_for_columns(xml_path, max_alt_count): tree = ET.parse(xml_path) root = tree.getroot() ns = {'mods': 'http://www.loc.gov/mods/v3'} data = {} alt_title_elements = root.findall('.//mods:titleInfo[@type="alternative"]/mods:title', ns) # 按列填充,实例不足时留空 for i in range(max_alt_count): if i < len(alt_title_elements) and alt_title_elements[i].text: data[f'alternative_title_{i+1}'] = alt_title_elements[i].text.strip() else: data[f'alternative_title_{i+1}'] = '' return data # 提取数据并导出 data_list = [extract_mods_data_for_columns(file, max_alt_title_count) for file in xml_files] def export_to_excel_columns(data_list, headers, excel_path): wb = openpyxl.Workbook() ws = wb.active ws.append(headers) for data in data_list: row = [data.get(header, '') for header in headers] ws.append(row) wb.save(excel_path) export_to_excel_columns(data_list, headers, 'output_columns.xlsx')
注意事项
- 使用
strip()去除文本前后空白,避免单元格内出现无效空格 - 若用换行符分隔内容,需在Excel中手动设置对应单元格的「自动换行」属性
- 若使用
lxml库替代xml.etree,处理逻辑完全一致,仅需修改导入语句为from lxml import etree
内容的提问来源于stack exchange,提问作者Spazman
相关产品推荐
相关产品推荐

