码上拾光

直读 OOXML 而不用现成库:需求文档解析的取舍

· #实现#文档解析#AI应用

今年的项目:客户的需求文档是 Word,要解析成需求条目树,写进项目管理工具,再让模型按条目生成测试用例。听起来解析 Word 是最简单的一步,实际是最花时间的一步。

现成库的问题

先用了最常见的 Python Word 库。跑了几份文档就发现三个拿不到的东西:

  1. 自动编号。 文档里的”3.2.1”很多是 Word 自动编号,库读出来的段落文本没有编号,只有”功能描述”四个字。层级信息全在编号里,没了编号就没了层级。
  2. 合并单元格。 需求表格大量用合并单元格表示分组。库读表格是按行列网格读的,合并的格子内容重复出现,结构对不上。
  3. 目录域。 有些文档的目录是 Word 自动生成的域,里面其实藏着完整的标题层级,库把它当普通文本。

三个问题都要绕到库的底层去改,不如直接读底层。

直读 OOXML

Word 文档就是个 zip,里面 document.xml 是正文,numbering.xml 是编号定义,styles.xml 是样式。用 lxml 直接解析,想要什么就取什么。

代价是要自己处理很多细节:编号的层级定义和实例引用是分开的,得关联起来算出实际编号;合并单元格要读 gridSpanvMerge;样式有继承链。这些都不难,就是多。

标题识别四个通道

标题是条目树的骨架,识别错了整棵树就错。单靠一个信号不够,做了四个通道:

通道信号可靠性
样式段落样式是 Heading 1/2/3高,但很多文档不用标题样式
编号域自动编号的层级高,但手打编号识别不到
目录目录域里的条目高,但目录可能过期
版式字号、加粗、缩进低,兜底

四个通道各出一份候选,按可靠性加权合并。冲突时高可靠通道优先,但一个通道单独判定的标题要有另一个通道佐证才采信。

自检页

解析完不直接入库,先生成一个自检页:左边原文,右边解析出的树,对应关系用颜色标。客户的需求工程师扫一遍,点确认才入库。

这一步是从图纸审核项目里的规则草稿箱学来的,原则一样:模型或规则的输出进正式流程前过一道人。

入库幂等

条目写入项目管理工具是逐条调接口,几百条要跑几分钟,中途可能断。做法是每写一条就在本地落一条进度记录,重跑时跳过已写的。同一份文档解析两次,不会建出两套工作项。

数字

后端一万三千多行,其中 OOXML 解析相关的将近一半。272 个测试用例,大部分是拿真实文档的片段做的回归。

值不值

直读 OOXML 多花了大概三周。换来的是:客户送来的文档不管怎么排版,基本都能解,解不了的在自检页一眼能看出来。用现成库的话,那三周会变成后面每个月的”这份文档又解不了”。

文档解析这种活,前期多投入是划算的,因为文档的花样永远比你想的多。


← 回到文章列表