DOCX文件本质是ZIP压缩包,含document.xml、styles.xml等XML文件,需解压后按命名空间解析,样式与编号须跨文件关联,盲目映射易出错。

DOCX 文件本质是 ZIP 压缩包,内部包含多个 XML 文件(如 document.xml、styles.xml、numbering.xml),直接“映射 XML 内容”没有通用标准——你得先明确想映射什么:是提取文字结构?还原样式逻辑?还是把 DOCX 的 XML 节点转成自定义 JSON 模型?否则任何“映射”都是盲操作。
用 Python 解压并定位关键 XML 文件
DOCX 不是纯 XML,而是 ZIP 封装。不能直接用 xml.etree.ElementTree 读取 .docx 文件本身,必须先解压或用 zipfile 随机访问。
-
document.xml在word/document.xml,存正文段落、运行(run)、文本(t)等核心内容 -
styles.xml在word/styles.xml,定义段落样式(w:pStyle)和字符样式(w:rStyle) -
numbering.xml在word/numbering.xml,控制编号列表逻辑,与document.xml中的w:numId关联 - 所有路径区分大小写,Windows 上可能不敏感,Linux/macOS 会报
KeyError
解析 document.xml 时注意命名空间(namespace)
Office Open XML 使用默认命名空间 https://www.php.cn/link/0f18e4824fc601cd270a4d31b084bb5d,几乎所有元素(如 w:p、w:t)都属于它。忽略命名空间会导致 find() 或 iter() 返回空。
import xml.etree.ElementTree as ET from zipfile import ZipFilewith ZipFile("example.docx") as docx: with docx.open("word/document.xml") as f: tree = ET.parse(f) root = tree.getroot()
必须声明命名空间,否则找不到节点
NS = {"w": "https://www.php.cn/link/0f18e4824fc601cd270a4d31b084bb5d"} paragraphs = root.findall(".//w:p", NS) # 正确
paragraphs = root.findall(".//p") # 错误:返回 []
for p in paragraphs: texts = [t.text for t in p.iterfind(".//w:t", NS) if t.text] print("".join(texts))
样式与编号需跨文件关联,不能只看 document.xml
DOCX 的样式不是内联属性,而是通过 ID 引用:一个段落有 w:pPr/w:pStyle[@w:val="Heading1"],但“Heading1”具体长什么样,得去 styles.xml 查;同理,编号 ID(w:numId)指向 numbering.xml 里的定义。硬编码匹配 ID 字符串极易出错。
- 用
lxml(支持 XPath 和更稳的命名空间处理)比原生xml.etree更可靠 - 不要手动拼接
numId和abstractNumId,它们是两级映射,numbering.xml里有w:abstractNum和w:num两个层级 - 若需还原“标题 1 → HTML
”,建议建映射表:
{"Heading1": "h1", "Normal": "p"},而非解析styles.xml的全部格式细节
真正难的不是读 XML,而是理解 DOCX 的隐式依赖链:一个带编号的标题段落,可能同时牵扯 document.xml(内容+ID)、styles.xml(标题样式)、numbering.xml(编号格式)、甚至 fonts.xml(字体 fallback)。没理清这个链条就写映射逻辑,十有八九漏掉缩进、多级列表或条件样式。










