如何用python-docx提取Docx标题间内容并维护标题层级
搞定这个问题其实不难!你已经迈出了第一步——成功提取标题,接下来只需要跟踪标题层级,把每个标题对应的内容(直到下一个同级/更高级标题)关联起来就行。我给你一套实用的方案:
核心思路
我们可以遍历文档的所有段落,用一个列表跟踪当前的标题层级路径(比如[一级标题, 二级标题]):
- 遇到标题时,先调整层级路径(弹出比当前层级高的标题),然后把当前标题加到对应父标题的子节点里
- 遇到普通段落时,直接把它加到当前最内层标题的内容列表中
完整代码实现
from docx import Document def build_document_structure(doc_path): doc = Document(doc_path) # 最终的文档结构,根节点是一级标题列表 doc_structure = [] # 跟踪当前所处的标题层级链,比如 [heading1_entry, heading2_entry] current_heading_chain = [] for para in doc.paragraphs: style_name = para.style.name if style_name.startswith('Heading'): # 解析标题层级(从样式名中提取,比如"Heading 2"对应层级2) heading_level = int(style_name.split()[-1]) # 调整层级链:移除比当前层级高的标题,找到父标题 while len(current_heading_chain) >= heading_level: current_heading_chain.pop() # 创建当前标题的条目,包含层级、文本、内容和子标题 heading_entry = { 'level': heading_level, 'text': para.text, 'content': [], 'children': [] } # 将当前标题添加到父节点或根结构 if current_heading_chain: current_heading_chain[-1]['children'].append(heading_entry) else: doc_structure.append(heading_entry) # 更新当前层级链 current_heading_chain.append(heading_entry) else: # 普通段落:添加到当前最内层标题的内容中 if current_heading_chain: current_heading_chain[-1]['content'].append(para.text) return doc_structure # 测试代码:打印文档结构 def print_doc_structure(structure, indent=0): for entry in structure: # 打印标题 print(f"{' '*indent}📌 Level {entry['level']}: {entry['text']}") # 打印标题下的内容 if entry['content']: print(f"{' '*(indent+1)}📝 Content:") for content_para in entry['content']: print(f"{' '*(indent+2)}- {content_para}") # 递归打印子标题 if entry['children']: print_doc_structure(entry['children'], indent+1) # 使用示例 if __name__ == "__main__": structure = build_document_structure('headerEX.docx') print_doc_structure(structure)
关键细节说明
- 层级处理逻辑:通过
current_heading_chain维护当前的标题路径,遇到新标题时,先弹出链中层级大于等于当前层级的标题,确保找到正确的父节点。比如遇到三级标题时,链中只会保留一级和二级标题。 - 结构存储:用嵌套字典保存每个标题的层级、文本、下属内容和子标题,完美还原文档的层级关系。
- 样式兼容性:代码默认识别内置的
Heading 1-Heading 9样式,如果你的文档用了自定义标题样式,只需要修改startswith('Heading')的判断条件(比如改成style_name == "自定义标题1")即可。
扩展提示
如果你的文档包含表格、图片等非段落内容,可以扩展逻辑:遍历doc.tables和doc.inline_shapes,通过它们在文档中的位置(比如对比段落的索引)判断属于哪个标题下的内容。
内容的提问来源于stack exchange,提问作者Nisarg Mankad




