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)
待解决问题
- 能否通过Python访问DOCX文件底层的编号信息?
- 是否有可处理该问题的Python库或工具?
- 若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文本:
需提前安装pandoc(通过官网下载或包管理器安装)。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'))
3. 中间步骤或格式转换方案
如果纯Python方法复杂度较高,可采用以下转换方式:
- DOCX转Markdown:用pandoc转换后,Markdown的列表编号会被完整保留,再通过
markdown等Python库提取文本和编号。 - DOCX转ODT:部分工具对ODT的编号解析更友好,转换后使用
python-odf库提取带编号的文本。
内容的提问来源于stack exchange,提问作者Anshuman Sharma
相关产品推荐
相关产品推荐

