关于本站
1、基于Django+Bootstrap开发
2、主要发表本人的技术原创博客
3、本站于 2015-12-01 开始建站
这是一件“悲哀”的事情,说出来给大家乐乐。
由于项目需要,需要用Python对word文档进行读写。我没多想,屁颠屁颠到pypi找了word操作库——python-docx。
刚开始用觉得还挺好用。后来随着项目深入,需要处理的word文件越来越大。
这个python-docx库的肩膀太小,负担不起。
因为它处理的东西都是放在内存里面,而且每次读写好像要跑个马拉松。文件越大,效率越慢。
没办法,我只能采用win32com组件调用word的com组件的方案。这个方案需要系统环境是windows,而且需要安装Office。所以开始就没考虑这个。(win32com组件可以在https://sourceforge.net/projects/pywin32/files/pywin32下载,我选的是220版本。根据系统和Python版本下载了64位的python2.7那个)
本篇博文,主要不是说如何用win32com读写word文档。
用win32com对word操作和写vba代码差不多,语法基本一致。简单贴上win32com调用word示例代码:
#coding:utf-8 from win32com.client import Dispatch def test(doc_full_path): table_count = 0 try: word = Dispatch('word.application') doc = word.documents.Open(doc_full_path) #获取该文档的表格数 table_count = doc.tables.count #关闭 doc.Close() except Exception as e: print(e.message) finally: #对com操作,一定要确保退出word应用 if word: word.Quit() return table_count
上面的test方法是给定word文档的路径,获取该文档里面表格的数量。
我在cmd命令行测试,发现word.Quit()执行之后,word的进程还存在!
我还可以重新再将这个应用显示出来。为了保险起见,加了一句删除变量的代码,才真正退出word进程。
del(word)
不过一般在程序中执行没有这个问题。因为该方法执行完成之后,会自动清理内存,删除没有使用的变量等。
单单这段代码测试一点问题都没有。但放入到总程序里面就出现问题了!返回错误信息说没有CoInitialize初始化。
我写的总程序是一个总控制脚本,里面用到多线程。我就怀疑是不是多线程导致的问题。
搜索一下,发现确实和多线程有关系。
在多线程里面使用win32com调用com组件的时候,需要用pythoncom.CoInitialize初始化一下。
最后还需要用pythoncom.CoUninitialize释放资源。
上面代码改成这样:
#coding:utf-8 from win32com.client import Dispatch import pythoncom def test(doc_full_path): table_count = 0 pythoncom.CoInitialize() try: word = Dispatch('word.application') doc = word.documents.Open(doc_full_path) #获取该文档的表格数 table_count = doc.tables.count #关闭 doc.Close() except Exception, e: print e.message finally: #对com操作,一定要确保退出word应用 if word: word.Quit() #释放资源 pythoncom.CoUninitialize() return table_count
修改之后,无论单独执行还是放到多线程里面执行都没问题了。
那这个CoInitialize是何方神圣?加了这个东西之后,在多线程就可以正常运行。
Coinitialize是Windows提供的API函数(好吧,还是微软惹的“祸”),用来告诉Windows系统单独一个线程创建COM对象。也就是说我这个多线程的脚本里面的线程和这个COM对象的线程创建一个套间,令其可以正常关联和执行。
看不懂?好吧,我也不是很清楚原理。反正,多线程使用win32com,就得加上这个。不喜欢Windows系统,问题多多。
另外,还有一个坑。
若我先手动打开一个Word文件,这时就运行了一个word进程。若执行Dispatch创建进程。
word = Dispatch('word.application')
再设置word不可见。
word.visible = False
但,我手动打开的Word进程的界面被隐藏了。
这说明一个问题,Dispatch若发现该进程已经存在的话,就不会创建新的进程。
若不创建新的进程,有些操作会有冲突,可能会影响到已经打开的Word文件。
这时,不能再使用Dispatch,用如下代码:
from win32com.client import DispatchEx word = DispatchEx('word.application')
即可真正创建新进程,无论是否已经打开了一样的进程。
杨仕航
2017-02-13更新本文,添加两个使用的注意事项
2017-02-13 09:28 回复