提取带格式Word文档内容:python-docx获取字体信息返回None问题
提取Word文档格式信息的问题与解决方案
问题背景
我需要提取Word文档中包含字体、字号、图片、批注等元素的格式信息,已经用zipfile模块将文档解压为XML文件,得到的文件列表如下:
['[Content_Types].xml', '_rels/.rels', 'word/_rels/document.xml.rels', 'word/document.xml', 'word/footer2.xml', 'word/header1.xml', 'word/footer1.xml', 'word/endnotes.xml', 'word/footnotes.xml', 'word/_rels/header1.xml.rels', 'word/header2.xml', 'word/_rels/header2.xml.rels', 'word/embeddings/Microsoft_Word_97_-_2003_Document1.doc', 'word/media/image3.wmf', 'word/media/image2.emf', 'word/theme/theme1.xml', 'word/media/image1.png', 'word/embeddings/oleObject1.bin', 'word/comments.xml', 'word/settings.xml', 'word/styles.xml', 'customXml/itemProps1.xml', 'word/numbering.xml', 'customXml/_rels/item1.xml.rels', 'customXml/item1.xml', 'docProps/app.xml', 'word/stylesWithEffects.xml', 'word/webSettings.xml', 'word/fontTable.xml', 'docProps/core.xml', 'docProps/custom.xml']
但我搞不懂word/document.xml里的内容和样式是如何关联的,最终希望把提取结果封装成这样的字典结构:
{ "text": "some-text-in-document", "font": "some-font", "font_size": 10, "some_field": "some-more-value", ... }
我尝试用python-docx获取字体和字号,但返回值大多是None,代码片段如下:
from docx.enum.style import WD_STYLE_TYPE styles = document.styles #print(styles.default) paragraph_styles = [s for s in styles if s.type == WD_STYLE_TYPE.PARAGRAPH] for style in paragraph_styles: #print(style.font.name) if(style.font.name): print(style.font.name, style.font.size) for paragraph in document.paragraphs: #print(paragraph.text) for run in paragraph.runs: print(run.text) font = run.style.font print(font.size)
解决思路与方案
1. 修正python-docx代码,正确获取字体信息
你得到None的核心原因是:Word的格式分直接应用到Run的字符格式和通过样式继承的格式,之前的代码只读取了run.style.font(样式里的格式),但很多时候格式是直接设置在Run本身的。另外,字号返回的是Pt对象,需要转换为数值。
调整后的代码可以同时获取直接格式和样式继承的格式:
from docx import Document def extract_run_format(run): # 优先取直接应用的格式,没有则 fallback 到样式中的设置 font_name = run.font.name or run.style.font.name # 转换字号为磅值(docx返回的Pt对象需要取.pt属性) font_size = run.font.size.pt if run.font.size else (run.style.font.size.pt if run.style.font.size else None) # 可扩展提取粗体、斜体等其他格式 return { "text": run.text.strip(), "font": font_name, "font_size": font_size, "bold": run.font.bold or run.style.font.bold, "italic": run.font.italic or run.style.font.italic } # 替换成你的文档路径 doc = Document("your_document.docx") formatted_data = [] # 遍历所有段落和Run,过滤空文本 for para in doc.paragraphs: for run in para.runs: if run.text.strip(): formatted_data.append(extract_run_format(run)) # 输出提取结果 for item in formatted_data: print(item)
2. 直接解析XML文件,关联样式与内容
如果要深入处理XML文件,需要结合word/styles.xml和word/document.xml:前者定义了所有样式的属性,后者的文本节点通过styleId引用样式。这里用lxml库来解析:
from lxml import etree # 定义Word的XML命名空间 NS = {"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"} # 第一步:解析styles.xml,建立样式ID到属性的映射 style_tree = etree.parse("word/styles.xml") style_map = {} for style in style_tree.xpath("//w:style", namespaces=NS): style_id = style.get(f"{{{NS['w']}}}styleId") # 提取字体名称 font_name_node = style.xpath(".//w:name", namespaces=NS) font_name = font_name_node[0].get(f"{{{NS['w']}}}val") if font_name_node else None # 提取字号(Word用半磅为单位,需除以2得到实际磅值) font_size_node = style.xpath(".//w:sz/@w:val", namespaces=NS) font_size = int(font_size_node[0])/2 if font_size_node else None style_map[style_id] = {"font": font_name, "font_size": font_size} # 第二步:解析document.xml,提取文本并匹配样式 doc_tree = etree.parse("word/document.xml") extracted_items = [] for run in doc_tree.xpath("//w:r", namespaces=NS): # 提取Run中的文本 text = "".join(run.xpath(".//w:t/text()", namespaces=NS)).strip() if not text: continue # 获取Run引用的样式ID style_id_node = run.xpath(".//w:rStyle/@w:val", namespaces=NS) style_info = style_map.get(style_id_node[0]) if style_id_node else {} # 检查直接应用的格式(优先级高于样式) direct_font = run.xpath(".//w:rFonts/@w:ascii", namespaces=NS) direct_size = run.xpath(".//w:sz/@w:val", namespaces=NS) # 组装结果 item = { "text": text, "font": direct_font[0] if direct_font else style_info.get("font"), "font_size": int(direct_size[0])/2 if direct_size else style_info.get("font_size") } extracted_items.append(item) # 输出结果 for item in extracted_items: print(item)
3. 扩展处理图片、批注等元素
- 图片:查看
word/_rels/document.xml.rels,里面记录了图片的关联路径(对应word/media/下的文件),再解析document.xml中的w:drawing节点,可以获取图片在文档中的位置信息。 - 批注:直接解析
word/comments.xml,里面包含批注的文本、作者和关联的段落ID,通过ID可以和document.xml中的内容对应起来。
内容的提问来源于stack exchange,提问作者Asif Ali




