阅读文本 / Reading text
本文使用合成样本演示处理方法。代码已通过隔离环境中的示例检查;这不代表对所有真实文件或产品效果的保证。处理重要文件前,请保留原件,并按文中的边界与验收项复核。
当你用脚本提取 Word 文档目录时,发现某个看起来像“一级标题”的段落被漏掉了,或者把一段加粗的正文误判为章节。这通常不是正则写得不够好,而是该段落根本没有应用
Heading 1
样式,只是手动调整了字号和加粗。在 Word 工程里,视觉表现不等于文档结构,只有
paragraph.style
才是可靠的语义线索。
为什么必须使用英文内部名查找样式
python-docx
操作的是底层的 WordprocessingML 文件,而非 Word 应用程序界面。这意味着无论用户的 Word 是中文、日文还是德文版,内置样式的存储名称永远是英文。例如,中文版 Word 界面上显示的“标题 1”,在 XML 中始终记录为
Heading 1
。
如果在代码中使用
styles['标题 1']
进行查找,程序会直接抛出
KeyError
。这是处理多语言文档时最常见的陷阱之一。自定义样式(Custom Styles)则不受此限制,它们的名称在 XML 中与用户在界面上看到的完全一致,不会发生本地化转换。因此,编写稳健的解析逻辑时,必须建立一个从本地化显示名到英文内部名的映射表,或者强制要求文档提交者使用标准模板。
合成示例:正确输出与边界情况处理
为了演示样式识别的逻辑,我们构造一个包含三种典型段落的合成样本。这个样本不依赖外部文件,直接在内存中生成,便于复现和理解。
合成示例输入:
1. 段落A:应用了
Heading 1
样式,文本为“第一章 引言”。
2. 段落B:样式为
Normal
,但手动设置了 16pt 字号和加粗,文本为“重要提示”。
3. 段落C:应用了自定义样式
MyCustomTitle
,文本为“附录说明”。
预期正确输出:
解析器应返回一个结构化列表,仅将段落A和段落C标记为标题节点。段落B虽然视觉上突出,但因缺乏语义样式,应被归类为普通正文或标记为“疑似标题待人工确认”,绝不能自动提升为结构节点。
错误/边界输入及处理:
若文档中某段落引用的样式已被删除(即 XML 中存在
w:pStyle w:val="DeletedStyle"
但样式定义缺失),
python-docx
访问
paragraph.style.name
时可能返回
None
或抛出异常。健壮的处理方式是在访问前检查样式对象是否存在,或将此类段落降级为纯文本处理,而不是让整个解析流程崩溃。
内存构建样本与样式断言代码
以下代码展示了如何在受控环境中验证样式识别逻辑。请注意,这段代码旨在演示 API 用法和断言思路,并非生产级解析器。实际使用时需在你自己的锁定依赖环境中运行,推荐
python-docx>=0.8.11
。
from docx import Document
from docx.enum.style import WD_STYLE_TYPE
from docx.shared import Pt
def build_sample_doc():
"""构建含混合样式的合成文档,无需外部文件"""
doc = Document()
# 标准内置标题
doc.add_heading('Chapter One', level=1)
# 视觉像标题但无样式
p_visual = doc.add_paragraph('Visual Fake Title')
run = p_visual.runs[0]
run.bold = True
run.font.size = Pt(15) # 使用Pt单位确保API规范
# 自定义样式
if 'MyCustomTitle' not in [s.name for s in doc.styles]:
doc.styles.add_style('MyCustomTitle', WD_STYLE_TYPE.PARAGRAPH)
doc.add_paragraph('Appendix Note', style='MyCustomTitle')
return doc
def extract_semantic_headings(doc):
"""仅提取具有明确样式的标题,忽略直接格式化"""
headings = []
for para in doc.paragraphs:
style_name = para.style.name if para.style else None
# 严格匹配:内置Heading或已知自定义标题样式
if style_name and (style_name.startswith('Heading ') or style_name == 'MyCustomTitle'):
headings.append({'text': para.text, 'style': style_name})
return headings
# 验证逻辑
sample = build_sample_doc()
result = extract_semantic_headings(sample)
assert len(result) == 2, f"期望2个语义标题,实际得到{len(result)}个"
assert result[0]['style'] == 'Heading 1'
assert result[1]['style'] == 'MyCustomTitle'
assert all('Visual Fake' not in item['text'] for item in result)
这段代码明确排除了仅靠字体属性判断标题的做法。
assert
语句用于确保逻辑符合预期,但在真实文档中,你可能需要将断言替换为日志记录或异常收集机制。
验收清单与人工审查边界
自动化解析永远无法覆盖所有排版习惯。下表总结了常见场景的处理决策,帮助团队划定机器与人工的协作边界。
观察现象
处理决定
是否需人工审查
段落样式为
Heading X
直接作为结构节点提取
否
样式为
Normal
但字号≥14pt且加粗
标记为“疑似标题”,保留原文位置信息
是
样式名包含“标题”但非内置(如“章节标题”)
查映射表;若无对应则视为正文
视映射完整性而定
paragraph.style
为
None
记录警告,按纯文本处理
是(批量出现时)
自定义样式继承自
Heading 1
可通过
base_style
链追溯,视为有效标题
否(若继承链完整)
注意:此表格针对“代码演示”层面的逻辑校验。端到端效果还需结合具体文档类型评估。例如,法律合同中的“定义条款”可能有专用样式,不应被当作章节标题;而技术手册中的“注意事项”虽用
Heading 3
,在翻译时可能需要特殊处理。遇到这些领域特定规则,必须引入人工审查环节。
参考资料与下一步行动
Working with Styles - python-docx 官方文档
Understanding Styles - python-docx 概念说明
将这份检查清单用于文档翻译
Word 翻译容易把内容问题与格式问题混在一起。把样式、页眉和表格等检查项留到交付清单里,让审校知道该在哪里检查。
OmniDoc(产品入口)
提供多格式文档翻译入口,官网介绍了 OCR、术语表与双语对照功能。对希望直接从文件开始处理的团队,这是一条值得用实际样本评估的路径。请使用有权上传的脱敏文件,并用本篇的检查项复核结果;这里不声称 OmniDoc 使用了本文的开源库或内部算法,也不以示例代替产品性能测试。
- 1 -
更新于 2026-10-09