如何用Python从docx提取带编号的格式化数据并转为JSON
解决DOCX带编号段落提取并转为JSON的问题
我之前也踩过这个坑——用para.text只能拿到段落里的纯文本,完全拿不到Word里设置的那些编号(比如A、1.1、罗马数字I这些)。要实现你要的JSON结构,得用python-docx库的深层属性来提取编号和对应内容,下面给你具体的解决方案:
步骤1:安装依赖
首先确保你装了python-docx,如果没装的话,执行:
pip install python-docx
步骤2:核心代码实现
这段代码会遍历DOCX里的段落,提取带编号的项,整理成你要的字典,最后转成JSON:
import docx import json from docx.enum.style import WD_STYLE_TYPE def extract_numbered_paragraphs(doc_path): doc = docx.Document(doc_path) result = {} for para in doc.paragraphs: # 跳过空段落 if not para.text.strip(): continue # 判断段落是否属于编号/列表样式 if para.style.type == WD_STYLE_TYPE.LIST: num_text = "" # 优先从XML结构提取编号(更准确) num_pr = para._element.xpath(".//w:numPr") if num_pr: try: num_id = num_pr[0].xpath(".//w:numId")[0].attrib.get("{http://schemas.openxmlformats.org/wordprocessingml/2006/main}val") num_def = doc.part.numbering_part.numbering_definitions._numbering_definitions num = num_def.get_num(int(num_id)) if num: level = int(num_pr[0].xpath(".//w:ilvl")[0].attrib.get("{http://schemas.openxmlformats.org/wordprocessingml/2006/main}val")) num_format = num.level(level).num_format.val # 获取当前编号值 num_val_elem = para._element.xpath(".//w:numPr/w:numStart") num_val = num_val_elem[0].attrib.get("{http://schemas.openxmlformats.org/wordprocessingml/2006/main}val") if num_val_elem else "" # 根据编号格式生成对应文本 if num_format == "upperLetter": num_text = chr(ord('A') + int(num_val) - 1) elif num_format == "decimal": # 处理多级编号(比如1.1),直接从文本前缀提取更简单 num_text = para.text.split()[0].rstrip('.') elif num_format == "upperRoman": # 数字转罗马数字 def int_to_roman(num): val = [(1000, 'M'), (900, 'CM'), (500, 'D'), (400, 'CD'), (100, 'C'), (90, 'XC'), (50, 'L'), (40, 'XL'), (10, 'X'), (9, 'IX'), (5, 'V'), (4, 'IV'), (1, 'I')] roman = '' while num > 0: for i, r in val: while num >= i: roman += r num -= i return roman num_text = int_to_roman(int(num_val)) except Exception: # XML解析失败时,从文本开头提取编号 pass # 兜底方案:直接从文本开头截取编号 if not num_text: first_part = para.text.split()[0] if first_part.endswith('.'): num_text = first_part.rstrip('.') # 提取编号后的内容 content = para.text.replace(f"{num_text}.", "").strip() if num_text: result[num_text] = content return result # 调用示例 doc_path = "your_document.docx" extracted_data = extract_numbered_paragraphs(doc_path) # 转为JSON格式 json_output = json.dumps(extracted_data, ensure_ascii=False, indent=4) print(json_output)
关键说明
- 代码优先从Word的XML结构提取编号(更准确,适配复杂的编号设置),如果解析失败,会自动 fallback 到从文本开头提取编号(适配你给出的示例格式)
- 针对大写字母、十进制、罗马数字三种编号格式做了专门处理,能生成你需要的键名(A、1.1、I等)
- 最终输出的字典可以直接转为格式化的JSON字符串,和你预期的结构一致
测试你给出的示例文档,应该能得到和你预期一致的输出结果。
内容的提问来源于stack exchange,提问作者Pankaj Singh




