Word文档XML中多级列表编号文本缺失问题及查找方法咨询
解析Word XML时丢失多级列表编号的原因及解决方法
我用Python的xml.etree.ElementTree模块解析Word文档的XML内容,通过以下代码提取document.xml并保存为TXT文件:
from tkinter import filedialog import zipfile import xml.etree.ElementTree as ET file_path = filedialog.askopenfilename() def get_export_raw_xml(doc_filename): raw_xml = zipfile.ZipFile(doc_filename).read('word/document.xml') temp_root = ET.fromstring(raw_xml) FileXML = open("TempDocXML.txt", "w") FileXML.write(str(ET.tostring(temp_root))) FileXML.close() get_export_raw_xml(file_path)
提取后的XML里只能看到多级列表的标题内容(如“GENERAL”“First Section”),找不到定义的多级列表编号文本(如“CHAPTER 1—”“Section 1.01”)。
原因
Word的多级列表编号并非直接存储在document.xml的正文内容里,而是单独存放在word/numbering.xml文件中。document.xml里的标题段落只会引用对应的列表编号ID和级别,不会直接包含编号文本。Word渲染文档时,会根据numbering.xml里的规则和段落的引用信息,动态生成实际显示的编号文本。
解决方法
要获取完整的编号文本,需要同时解析numbering.xml和document.xml,步骤如下:
- 从Word压缩包中提取
numbering.xml,解析出所有列表编号的规则(包括前缀、编号格式等); - 在
document.xml中找到每个标题段落对应的列表ID和级别,匹配numbering.xml中的对应规则; - 根据规则拼接编号文本和标题内容。
修改后的代码示例
from tkinter import filedialog import zipfile import xml.etree.ElementTree as ET # Word XML的命名空间,必须指定才能正确查找元素 NS = { 'w': 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' } file_path = filedialog.askopenfilename() def parse_numbering_rules(zip_file): # 解析numbering.xml,返回编号规则字典:(编号ID, 级别) -> 编号前缀/格式 numbering_content = zip_file.read('word/numbering.xml') root = ET.fromstring(numbering_content) num_rules = {} # 遍历每个编号定义 for num_node in root.findall('.//w:num', NS): num_id = num_node.get(f'{{{NS["w"]}}}numId') # 获取关联的抽象编号ID abstract_num_id = num_node.find('.//w:abstractNumId', NS).get(f'{{{NS["w"]}}}val') abstract_num_node = root.find(f'.//w:abstractNum[@w:abstractNumId="{abstract_num_id}"]', NS) # 遍历每个级别的规则 for level_node in abstract_num_node.findall('.//w:lvl', NS): level = level_node.get(f'{{{NS["w"]}}}ilvl') # 获取编号文本模板(比如"CHAPTER %1—") level_text = level_node.find('.//w:lvlText', NS) if level_text is not None: num_template = level_text.get(f'{{{NS["w"]}}}val') num_rules[(num_id, level)] = num_template return num_rules def export_doc_with_numbers(doc_filename): with zipfile.ZipFile(doc_filename) as zf: # 先解析编号规则 num_rules = parse_numbering_rules(zf) # 解析document.xml doc_content = zf.read('word/document.xml') doc_root = ET.fromstring(doc_content) # 保存带编号的文本 with open("DocWithNumbers.txt", "w", encoding='utf-8') as output_file: # 简单维护编号计数器,复杂多级编号需调整层级重置逻辑 num_counter = {} for para in doc_root.findall('.//w:p', NS): # 检查段落是否属于列表项 num_pr = para.find('.//w:numPr', NS) if num_pr is not None: num_id = num_pr.find('.//w:numId', NS).get(f'{{{NS["w"]}}}val') level = num_pr.find('.//w:ilvl', NS).get(f'{{{NS["w"]}}}val') # 获取对应的编号模板 num_template = num_rules.get((num_id, level), '') # 更新计数器 key = f"{num_id}_{level}" num_counter[key] = num_counter.get(key, 0) + 1 # 替换模板中的%1为当前编号值 number_text = num_template.replace('%1', str(num_counter[key])) # 提取段落文本 para_text = ''.join([t.text for t in para.findall('.//w:t', NS) if t.text]) if number_text and para_text: output_file.write(f"{number_text} {para_text}\n") else: output_file.write(f"{para_text}\n") else: # 普通段落直接写入文本 para_text = ''.join([t.text for t in para.findall('.//w:t', NS) if t.text]) if para_text: output_file.write(f"{para_text}\n") export_doc_with_numbers(file_path)
代码关键点说明
- 必须指定Word XML的命名空间,否则无法正确定位带
w:前缀的元素; parse_numbering_rules函数从numbering.xml中提取每个编号ID和级别对应的编号模板;- 示例用简单计数器处理编号递增,若要支持1.1、1.2这类嵌套多级编号,需根据层级关系调整计数器的重置逻辑;
- 最终将编号文本和标题内容拼接后写入TXT文件,就能得到完整的带编号的标题内容。
内容的提问来源于stack exchange,提问作者Salaam Hamad
相关产品推荐
相关产品推荐

