word排版工具 com接口太慢,换成了xml+com结合
-
对wps做了一些bug修复,速度上有效提升,解决了异常弹窗,用户可直接覆盖安装。
-
专门对word做了一个优化版本,必须要在word上用的话,现在只是略逊于wps
-
前阵子有个朋友找我,说他们公司IT政策很严,不让装WPS,也不让装任何第三方软件,电脑里只有微软Office。但他平时干活又需要处理大量Word文档,提取标题、整理结构这些事情,手动做到手抽筋。
我们之前做的Inkwell工具,WPS版跑得挺好的,但Word版因为依赖COM接口,遇到大文档就拉胯。他试了一下,五百页的标书,点个扫描,进度条爬得比蜗牛还慢,直接劝退。
原罪是COM
COM这东西,说白了就是Word这个程序和你写的脚本之间的“传话筒”。你每问一句“这段落是什么级别”,它就要跑到Word那边问一声,再跑回来告诉你。
一趟几十毫秒,五百页跑下来几千趟,几分钟就这么没了。
关键是,很多公司环境就是只有Word,没得选。所以这个问题必须解决,拖不得。
想过很多办法,都不太行
python-docx?能读段落,但拿不到大纲级别。只能靠样式名去猜,比如看到“Heading 1”就认为是标题。但用户的文档五花八门,很多人根本不按样式来,猜错一个目录就全歪了。
直接解压docx读XML?.docx本质上是个zip包,解出来里面的document.xml就是文档正文。这招速度确实快,但我们被表格坑惨了——表格里的段落,在XML里的顺序和COM接口的Paragraphs.Item(i)对不上,算出来的索引跳转老是偏移,定位不准。
最后的方案:XML负责快,COM负责准
后来转念一想,为什么非要二选一呢?各取所长不就行了。
直接解压docx,用lxml解析document.xml,遍历所有段落标签,大纲级别从w:outlineLvl属性里读。这一步全程不碰COM,速度快到飞起——五百页文档,一两秒跑完。
顺便用XML里的fldChar域标记把目录页自动跳过,比原来用COM算Range.Start省事多了。
标题捞出来了,但要在Word里给它们加书签、做跳转,这活只有COM能干。不过这时候COM的工作量已经小很多了——XML扫描的时候顺手把每个段落对应的COM索引算好,然后直接用Paragraphs.Item(index)精准定位。
原来要调用几千次COM,现在只调用几十次(标题的数量),开销降了两个数量级。
跟WPS版比,实话实说还是没跑赢。这里补充说明一下,WPS走的其实也是COM,不是有什么神秘的“原生API”。但WPS对COM的实现做了不少优化,接口响应就是比Office的COM快一截,这点得认。同一个文档,WPS版跑起来就是更轻快。
不过Word版至少从“完全没法用”变成了“基本够用”,不至于太丢人。
朋友反馈说现在能用起来了,我心里这块石头也算落地了。
说句实在话
虽然专门给Word做了一版优化,但如果你问我个人推荐哪个——我还是推荐WPS。
可能是我用习惯了,WPS在COM接口层面的优化确实比Office做得好,同样的调用逻辑跑起来就是顺畅。而且WPS对国内用户的一些使用场景(比如公文模板、排版习惯)照顾得更细致,这些是Office比不了的。当然,这纯粹是个人偏好。工具这东西,用着顺手就是最好的。关键是看你的环境允许用什么。
如果你的公司没有软件限制,我建议你先试试WPS版,体验确实更流畅。如果公司规定只能用Office,那这个Word优化版至少能让你不用对着进度条干瞪眼。
给同样折腾自动化的朋友
COM慢,但有些事只有它能干。XML快,但有些细节它搞不定。把两者结合起来,COM用在“不得不用的地方”,其他地方用XML替代,复杂度就从O(总段落数)降到了O(标题数)。
如果你的场景也是公司限制只能用Word,并且需要处理大文档的结构提取,这个思路供你参考。
工具本身我放到网上了,搜“Inkwell 琢墨”应该能找到,Word版和WPS版分开发布,按需取用。