You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用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

火山引擎 最新活动