You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python提取DOCX文件中格式化编号的解决方案求助

从DOCX文件提取带格式化编号的文本(Python方案)

背景与需求

正在开发Python项目,需从DOCX文件提取文本并保留格式化编号(如1.、1.1、1.1.1等)。手动复制粘贴为纯文本时编号可正常保留,说明编号信息存在,但现有代码方法无法捕获。

已尝试的无效方法

  • 使用python-docx库:仅提取文本,丢失编号格式
    from docx import Document
    
    doc = Document('my_file.docx')
    for para in doc.paragraphs:
        print(para.text)
    
  • 转换DOCX为HTML后解析:同样无法保留编号
    import mammoth
    from bs4 import BeautifulSoup
    
    with open("my_file.docx", "rb") as docx_file:
        result = mammoth.convert_to_html(docx_file)
        html = result.value
    
    soup = BeautifulSoup(html, 'html.parser')
    text = soup.get_text()
    print(text)
    
  • 使用docx2txt:仍丢失编号
    import docx2txt
    
    text = docx2txt.process("my_file.docx")
    print(text)
    

待解决问题

  1. 能否通过Python访问DOCX文件底层的编号信息?
  2. 是否有可处理该问题的Python库或工具?
  3. 若Python无直接方法,是否有中间步骤或格式转换可保留编号信息?

解决方案

1. 直接通过Python访问DOCX底层编号信息

可以。DOCX的编号存储在文档的numbering.xml部件中,python-docx提供了访问段落编号属性的底层接口,通过解析段落的XML元素可生成编号文本:

示例代码:

from docx import Document
from docx.oxml.ns import qn

def get_paragraph_number(paragraph):
    numbering_part = paragraph.part.numbering_part
    if not numbering_part:
        return ""
    
    pPr = paragraph._p.find(qn('w:pPr'))
    if not pPr:
        return ""
    
    numPr = pPr.find(qn('w:numPr'))
    if not numPr:
        return ""
    
    numId = numPr.find(qn('w:numId'))
    ilvl = numPr.find(qn('w:ilvl'))
    if not numId or not ilvl:
        return ""
    
    num_id = int(numId.get(qn('w:val')))
    ilvl_id = int(ilvl.get(qn('w:val')))
    
    num_def = numbering_part.numbering_definitions._get_num(num_id)
    if not num_def:
        return ""
    
    abstract_num_id = num_def.abstract_num_id.val
    abstract_num = numbering_part.numbering_definitions._get_abstract_num(abstract_num_id)
    if not abstract_num:
        return ""
    
    lvl = abstract_num.get_lvl(ilvl_id)
    if not lvl:
        return ""
    
    # 统计当前级别段落的序号
    count = 0
    for prev_para in paragraph.parent.paragraphs:
        if prev_para == paragraph:
            count +=1
            break
        prev_pPr = prev_para._p.find(qn('w:pPr'))
        if prev_pPr:
            prev_numPr = prev_pPr.find(qn('w:numPr'))
            if prev_numPr:
                prev_numId = prev_numPr.find(qn('w:numId'))
                prev_ilvl = prev_numPr.find(qn('w:ilvl'))
                if prev_numId and prev_ilvl:
                    if int(prev_numId.get(qn('w:val'))) == num_id and int(prev_ilvl.get(qn('w:val'))) == ilvl_id:
                        count +=1
    
    # 生成对应格式的编号文本(适配常见的多级十进制编号)
    format_str = lvl.numFmt.val
    if format_str == 'decimal':
        if ilvl_id == 0:
            return f"{count}."
        else:
            # 获取父级编号
            parent_ilvl = ilvl_id - 1
            parent_num = ""
            for prev_para in paragraph.parent.paragraphs:
                if prev_para == paragraph:
                    break
                prev_pPr = prev_para._p.find(qn('w:pPr'))
                if prev_pPr:
                    prev_numPr = prev_pPr.find(qn('w:numPr'))
                    if prev_numPr:
                        prev_numId = prev_numPr.find(qn('w:numId'))
                        prev_ilvl = prev_numPr.find(qn('w:ilvl'))
                        if prev_numId and prev_ilvl:
                            if int(prev_numId.get(qn('w:val'))) == num_id and int(prev_ilvl.get(qn('w:val'))) == parent_ilvl:
                                parent_num = get_paragraph_number(prev_para).rstrip('.')
                                break
            return f"{parent_num}.{count}."
    return ""

doc = Document('my_file.docx')
for para in doc.paragraphs:
    num_text = get_paragraph_number(para)
    print(f"{num_text}{para.text}")

2. 可用的Python库或工具

  • python-docx:通过上述底层XML访问方式实现,无需额外安装其他库。
  • pandoc:非纯Python库,但可通过subprocess调用,将DOCX转换为Markdown(会完整保留多级编号),再解析Markdown文本:
    import subprocess
    import os
    
    def docx_to_markdown(docx_path):
          md_path = docx_path.replace('.docx', '.md')
          subprocess.run(['pandoc', '-s', docx_path, '-o', md_path], check=True)
          with open(md_path, 'r', encoding='utf-8') as f:
              md_text = f.read()
          os.remove(md_path)
          return md_text
    
    print(docx_to_markdown('my_file.docx'))
    
    需提前安装pandoc(通过官网下载或包管理器安装)。

3. 中间步骤或格式转换方案

如果纯Python方法复杂度较高,可采用以下转换方式:

  • DOCX转Markdown:用pandoc转换后,Markdown的列表编号会被完整保留,再通过markdown等Python库提取文本和编号。
  • DOCX转ODT:部分工具对ODT的编号解析更友好,转换后使用python-odf库提取带编号的文本。

内容的提问来源于stack exchange,提问作者Anshuman Sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 08:52:28