概述

Python 中可以读取 word 文件的库有 python-docxpywin32

下表比较了各自的优缺点。

  优点 缺点
python-docx 跨平台 只能处理 .docx 格式,不能处理.doc格式
pywin32 仅限 windows 平台 .doc 和 .docx 都能处理

pywin32

这个库很强大,不仅仅可以读取 word,本文仅介绍其读取 word 功能。网上介绍用 pywin32 读取 .doc 的文章真不多,因为,真心不好用

以下是 pywin32 读取 .doc 的代码示例,但是读取表格有问题,输出全是空,原因不明,因为不打算用所以没有深入研究。另外,如果表格中有纵向合并单元格,会报错:“无法访问此集合中单独的行,因为表格有纵向合并的单元格。”

from win32com.client import Dispatch

word = Dispatch('Word.Application')     # 打开word应用程序
# word = DispatchEx('Word.Application') # 启动独立的进程
word.Visible = 0        # 后台运行,不显示
word.DisplayAlerts = 0  # 不警告 path = r'E:\abc\test.doc'
doc = word.Documents.Open(FileName=path, Encoding='gbk') for para in doc.paragraphs:
    print(para.Range.Text) for t in doc.Tables:
    for row in t.Rows:
        for cell in row.Cells:
            print(cell.Range.Text) doc.Close()
word.Quit

但是 pywin32 有另外一个功能,就是将 .doc 格式另存为 .docx 格式,这样我们就可以使用 python-docx 来处理了。

# 将 .doc 文件转成 .docx 
def doc2docx(path):
    w = win32com.client.Dispatch('Word.Application')
    w.Visible = 0
    w.DisplayAlerts = 0
    doc = w.Documents.Open(path)
    newpath = os.path.splitext(path)[0] + '.docx'
    doc.SaveAs(newpath, 12, False, "", True, "", False, False, False, False)
    doc.Close()
    w.Quit()
    os.remove(path)
    return newpath

python-docx

python-docx 可以按段落读取 word,对于表格,可以单独的提取,代码如下:

import docx

fn = r'E:\abc\test.docx'
doc = docx.Document(fn) for paragraph in doc.paragraphs:
        print(paragraph.text) for table in doc.tables:
    for row in table.rows:
        for cell in row.cells:
            print(cell.text)

对于纵向合并单元格,python-docx 的处理也很贴心。看下面的截图:

word 表格截图:

代码运行结果截图:
 

综上所述,对于大批量 word 文件的读取,我建议使用 python-docx 库,若是 .doc 文件,则用 pywin32 库将其转化为 .docx 文件,然后再调用 python-docx 库读取。

Word 未能引发事件

这是我遇到的一个实际问题,困扰了我半天时间。

我的爬虫在爬取到 .doc 文件之后,就通过上面的方法将其转为 .docx 格式,原本一切都好,下班挂机在跑,第二天来一看,报了这个错:pywintypes.com_error: (-2147352567, '发生意外。', (0, 'Microsoft Word', 'Word 未能引发事件。', 'D:\工具\Microsoft Office\Office12\2052\WDMAIN11.CHM', 25482, -2146822286), None)

我用报错的文件单独调试了 doc2docx 方法,并没有报错。网上查了这个错误,没有啥收获。

反复测试后发现总是那个网页报错,说明 bug 可以重现,那么问题到底出在哪里?

我将代码一行行删去,直到只留下执行到报错所必须的代码:

def get_winningbid_detail(url, name):
    r = requests.get(url)
    r.encoding = 'utf-8'
    html = r.text
    soup = BeautifulSoup(html, 'lxml')     ps = soup.find_all(text=re.compile('附件'))
    if len(ps) > 0:
        os.makedirs(os.path.join(download_dir, name), exist_ok=True)
        for p in ps:
            a_tab = p.find_next_sibling('a')
            if a_tab is not None:
                link = homepage + a_tab['href']
                localfilename = os.path.join(download_dir, name, a_tab.text)
                # print(localfilename)
                with open(localfilename, 'wb+') as sw:
                    sw.write(requests.get(link).content)
                if localfilename.endswith('.doc'):
                    doc2docx(localfilename)

反复读这段代码,并没有发现什么问题。

因为有些网页的附件名称是相同的,例如 "公告.doc",所以我按每个网页的标题(在总览页面爬到的)分文件夹放置下载的文件,所以方法中传了一个 name 参数,而如果 name 参数传空,则不会报错。

其实由此已经可以发现 bug 所在了,但我却没想到,又反复折腾了很久才发现,原来是文件名太长了。

在 windows 下面,单个文件名的长度限制是 25,完整的路径长度(如 E:\abc\test.doc )限制是 260。路径最后有一个字符串结束符 '\0' 要占掉一个字符,所以完整路径实际限长是259。**

 

最新文章

  1. [Monitor] 监控规则定义
  2. no 'object' file generated
  3. hdu 4006 The kth great number (优先队列)
  4. ubuntu tar 命令详细讲解
  5. python基础之模块之os模块
  6. EF6 在原有数据库中使用 CodeFirst 总复习(四、新建实体对象)
  7. GitHub创建SSH Keys
  8. 拓扑排序 HDU - 5695
  9. Jenkins:VMware虚拟机Linux系统的详细安装和使用教程
  10. mysql 增删改查基础操作的语法
  11. 算法-KMP
  12. 离线安装Cloudera Manager 5和CDH5(最新版5.9.3) 完全教程(四)数据库安装(单节点)
  13. 【漏洞分析】两个例子-数组溢出修改返回函数与strcpy覆盖周边内存地址
  14. [转]sqlserver2014两台不同服务器上数据库同步
  15. java.util.Vector排序
  16. Elasticsearch NEST – Examples for mapping between Query and C#
  17. 第十一章 Helm-kubernetes的包管理器(上)
  18. ACM2112迪克斯特算法
  19. win10 Edge 无法上网代理服务器错误
  20. 14_synchronized深入

热门文章

  1. 拾人牙慧篇之——基于HTML5中websocket来实现消息推送功能
  2. Hadoop生态圈初识
  3. Android 开发知识体系
  4. Servlet线程
  5. CSS 外边距合并。
  6. 0513JS数组的定义、遍历、添加
  7. 更新版PowerBI发布了-- Power BI Report Server Update – March 2018
  8. SQL之left join,inner join,right join
  9. PAT1096:Consecutive Factors
  10. PAT1132: Cut Integer