在之前的文章[1] [2]中,我用python-docx这个包读取外部数据,驱动了word文档的更新,相当于是实现了Excel数据往word文档的数据流动。实现的方式比起之前依靠vba用Excel驱动word更新这个文章中说的要更可靠一些。

最近遇到了另外一个问题:为了收集教师材料,向20多个同事发放了一份资料收集表。表格大概长下面这样。可以看到里面有多个子表,里面包含了发表的文章,在研的项目,获得的成果,诸如此类的内容。现在要把这20多份表格的内容分门别类的整合成多个工作表。

图1

这其实就是数据从word往excel的流动。同样的,也可以用python-docx包加上一点pandas来实现。主要分下面几步

1. 程序与文件的文件结构

工作文件夹

|- doc2docx.py

|- grab_docxcontent.py

|- doc_files\

|- word_doc1.doc

|- word_doc2.doc

...

|- word_doc20.doc

|- rst\

收集上来的word文档统一放在doc_files文件夹里,两个python代码放在工作文件夹中。

2. 把.doc文件转换成.docx文件

python-docx包就像名字说的,可以处理docx文件,但是doc文件没法处理。所以就需要把20多个word文档中的doc文件转换成docx文件。*2020年了,2020年了呀同志们,再过3年03版本的office这个余孽就已经20岁了啊,tm还有人在用doc格式啊!*不管怎么说,既然有问题,总是要想办法去解决。解决的方式也不难,这里主要写windows环境下的处理方式参考来源

下面的代码(doc2docx.py)主要用到了pywin32这个包,通过win32com控制系统里已经安装好的word程序实现doc往docx的转换。所以务必先用pip装好pywin32

import os

import win32com.client

basedir = 'doc_files'

word = win32com.client.Dispatch('Word.application')

word = win32com.client.Dispatch('Word.application')这一行通过win32com让python可以控制word程序,接下来会反复的调用word这个对象

从doc往docx的转换其实并不复杂,无非是先1. 打开一个word文档, 2. 另存为docx文件,然后3. 关闭这个word文档,在代码里也就是三行而已

doc = word.Documents.Open(ori_path_abs)

doc.SaveAs2(conv_path_abs, FileFormat=16)

doc.Close()

顺带一提,如果是想把docx转换成doc,那可以把fileformat设置成0,参考下面这里:

需要设置的无非就是word文档的路径(ori_path_abs),还有就是保存文档的路径(conv_path_abs)。不过需要注意的是,如果你把一个相对链接交给word.Documents.Open()方法,系统肯定报错,类似下面这样

'Microsoft Word'

'抱歉,找不到您的文件。是否可能被移动、重命名或删除?\r

(C:\\Users\\admin\\Documents\\...\\Test.doc)',

'wdmain11.chm', 24654,

-2146823114

不过只要转换成绝对路径就不会出问题了,具体是使用os.path.abs这个方法。

另外还要注意的是,当你打开一个word文档以后,软件会自动生成一个临时文件,临时文件的名称开头两个字符是~$,这种文件是没办法用python-docx处理的,所以要注意排除。除此以外因为我们的目的是把doc转换成docx,所以也要注意筛选一下文件的扩展名。综合考虑上面的这些东西以后,处理一篇文档的代码被写进一个函数中方便反复调用:

def conv_doc2docx(filename):

""":param filename: doc文档的文件名:return: None"""

# 用os.path.splitext把文件名和扩展名拆开来,分别存为filename_base和filename_ext

filename_base,filename_ext = os.path.splitext(filename)

# 文件名加上.docx扩展名就是转换以后的文件名了。当然实际的转换在后面完成

filename_conv = filename_base+'.docx'

# 下面两行是检验前面提到的两个问题

assert '~$' not in filename_base, f"~$ in filename:{filename}"

assert filename_ext=='.doc', f'{filename} should be .doc file'

# 下面一行用basedir目录和文件名形成word文档的相对链接

path = os.path.join(basedir,filename)

# 下面一行用basedir目录和文件名形成转换后word文档(docx)的相对链接

path_conv = os.path.join(basedir,filename_conv)

# 下面两行是把相对链接转换成绝对链接

ori_path_abs = os.path.abspath(path)

conv_path_abs = os.path.abspath(path_conv)

try:

doc = word.Documents.Open(ori_path_abs)

doc.SaveAs2(conv_path_abs, FileFormat=16)

doc.Close()

except Exception as e:

print(f'Fail to convert:{filename}')

print(e)

接下来就是用os.listdir和list comprehension筛出符合要求的word文档文件名,然后交给函数去处理了

doc_files = os.listdir('datafile')

doc_files = [d for d in doc_files if os.path.splitext(d)[1]=='.doc' and '~$' not in d]

for e in doc_files:

conv_doc2docx(e)

3. 提取word文档的信息

一个人填写一份文档,所有的子表都在这个文档里面,所以一个word文档就是与这个人相关的各种信息表的组织单位。另一方面,不同的表格需要用到的处理方法又是差不多的,也就是说有很多方法可以复用。这种情况最好就是定义一个类。

class DocProc:

def __init__(self,docpath):

self.doc = Document(docpath)

self.name = self.doc.tables[0].row_cells(0)[1].text

...

def get_txt(self,c):

# some cell have table in them

...

def rowcells2txt(self,rcs:list):

...

def row2dict(self,row,header):

""":param row:包含实际内容的row_cells[i]:param header: 表头row_cells[i]:return:T/F row have content; and the dict"""

...

def table2df(self,table):

...

上面这个类除了基本的__init__以外,还为了方便数据的提取定义了若干方法。接下来分别讲一下table2df

def table2df(self,table):

# 提取header行

header = table.row_cells(0)

# 获取总行数

rows = len(table.rows)

content = list()

# 从第2行开始,遍历剩下的所有行

for i in range(1,rows):

have_content, rst_dict = self.row2dict(table.row_cells(i), header)

if have_content:

content.append(rst_dict)

if len(content) == 0:

return None

df = pd.DataFrame(content)

df['name'] = self.name

return df

文档中的表格包含表头和实际内容两部分,所以首先提取header行,然后从第2行开始,遍历剩下的所有行,把每一行的内容都转换成dict,各行的dict又组成了list,最后使用pandas.DataFrame就可以把list转成数据框。而在返回数据框以前,加上一列姓名,标注这一个表格是从哪一位职工交上来的。

上面遍历的过程中将一行表格转成dict的操作是利用row2dict这个方法实现的。row2dict

def row2dict(self,row,header):

""":param row:包含实际内容的row_cells[i]:param header: 表头row_cells[i]:return: T/F whether row has content; and the dict"""

row = self.rowcells2txt(row)

row_empty_cells = [r for r in row if r=='']

if len(row)-len(row_empty_cells) <= 1:

return False, None

header = self.rowcells2txt(header)

rst_dict = dict()

for i,e in enumerate(header):

rst_dict[e] = row[i]

return True, rst_dict

照理来说,这个方法主要负责把一系列表头格子和一系列内容行的格子一一配对,不过在配对以前要注意内容行是否是空的。如果传入的这一行没有内容,那么直接返回None。为了标注是否存在内容,上面的代码额外加了一个布尔返回值。

上面的代码中,将一行从cells类型转成字符串的list这个操作是通过rowcells2txt函数实现的rowcells2txt与get_txt

def rowcells2txt(self,rcs:list):

return [self.get_txt(c) for c in rcs]

可以看到转换过程倒也简单,只是把cells中的每个元素都交给get_txt过一遍。而这个方法代码如下:

def get_txt(self,c):

# some cell have table in them

if len(c.tables)==0:

return c.text

else:

return "table_within!!"

之所以要额外写一个方法,是因为有时候别人在填表的时候,会直接复制粘贴,这样一来可能会出现一个单元格内嵌入一个表格的情况。如果简单的返回单元格的text属性就会得到空值,这样一来甚至可能出现数据的遗漏。为了避免这个情况,这里会检查一下单元格下面是否有table,如果没有的话(len(c.tables)==0)那就可以照常获取text属性,否则就返回一个警告提示作为内容。后期处理的时候对于这些行就能有针对性的处理。

上面这些准备做完以后,我就直接在init里面完成必要的工作了__init__

def __init__(self,docpath):

self.doc = Document(docpath)

self.name = self.doc.tables[0].row_cells(0)[1].text

table_articles = self.doc.tables[4]

self.df_articles = self.table2df(table_articles)

table_funds = self.doc.tables[5]

self.df_funds = self.table2df(table_funds)

table_patent = self.doc.tables[6]

self.df_patent = self.table2df(table_patent)

这里大部分都是流程性的工作,首先是从第一个表格的第一行第2列获得姓名(表格当时就是这样设计的),然后把第5个表格转换成数据框,作为这个职工的文章信息,类似的提取项目信息和专利信息。最后就是依靠pd.concat进行合并了:

docprocs = [DocProc(path) for path in doc_files]

articles = [docproc.df_articles for docproc in docprocs]

funds = [docproc.df_funds for docproc in docprocs]

patents = [docproc.df_patent for docproc in docprocs]

df_articles = pd.concat(articles)

df_funds = pd.concat(funds)

df_patents = pd.concat(patents)

xlwriter = pd.ExcelWriter(path=os.path.join('rst','202006骨干情况调查表4到6.xlsx'),engine='xlsxwriter')

df_articles.to_excel(xlwriter,sheet_name='发表文章')

df_funds.to_excel(xlwriter,sheet_name='课题')

df_patents.to_excel(xlwriter,sheet_name='专利')

xlwriter.save()

python读取word文件内容_[python]读取word文档中的数据,整理成excel表相关推荐

  1. 如何把word文档中的表格转成excel表格

    word的表格转成excel教程 1.打开文件,按键盘Fn+F12键 2.弹出这个界面,点击保存类型,选择网页(*.html) 这个类型,点保存到桌面 3.桌面新建excel表格,打开excel,把网 ...

  2. POI:从Excel文件中读取数据,向Excel文件中写入数据,将Excel表格中的数据插入数据库,将数据库中的数据添加到Excel表

    POI 简介: POI是Apache软件基金会用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能. ...

  3. 【.NET】用Aspose.Words for .NET动态生成word文档中的数据表格

    1.概述 最近项目中有一个这样的需求:导出word 文档,要求这个文档的格式不是固定的,用户可以随便的调整,导出内容中的数据表格列是动态的,例如要求导出姓名和性别,你就要导出这两列的数据,而且这个文档 ...

  4. 批量处理word文件内容_用python批量提取word文件信息,导出到excel文件

    技术的运用可以解决大量重复处理的工作,提高效率. 比如,有大量的论文电子文档(.docx格式),需要提取文档中的题目.作者.单位等信息制成表格(.xlsx格式),一般每篇论文的题目在第1行,副标题在第 ...

  5. python下载图片到文件夹_python实现解析markdown文档中的图片,并且保存到本地~

    背景 前阵子简书好像说是凉了,搞得我有点小慌,毕竟我的大部分博客都是放在简书上面的,虽然简书提供了打包导出功能,但是只能导出文字,图片的话还是存在简书服务器上面,再加上我一直想要重新做一个个人博客,于 ...

  6. C语言读取txt文档中的数据

    1.说明    txt文档中的数据格式:前后数据用空格隔开:    数据来源:matlab读取彩图的R.G.B三层的像素值,分别存放在三个txt文档中,用C读取到一维数组.    动态申请数组,还是需 ...

  7. 不用POI技术,JAVA给Word文档中的数据区域赋值

    在本地磁盘上打开一个Word文件,添加内容或插入图片是十分容易的,但是如何在线打开一个Word文件并修改Word文件内容或者插入图片呢,这时就需要通过PageOffice开发平台,给数据区域赋值,来实 ...

  8. 查找和替换文本和其他 Word 文档中的数据(转)

    今天看一段vba代码,目的去除段首空格,替换的时候查找的文字是^w,不知其意,然后搜索去了 参考:http://zhidao.baidu.com/link?url=dPoOamRVLkY-WpD6v5 ...

  9. 谷歌浏览器在新页面打开_如何在Google文档中更改页面方向

    谷歌浏览器在新页面打开 Most of the time, using a portrait orientation for document pages makes sense. Occasiona ...

  10. 在中国使用谷歌语音识别_如何在Google文档中使用语音输入

    在中国使用谷歌语音识别 Google Docs lets you use voice typing to dictate using your computer's microphone. It's ...

最新文章

  1. electron 利用 electron-builder实现自动更新
  2. python 数字转十六进制_在Python中将整数转换为十六进制
  3. 这是云代驾,不是打游戏
  4. Codeforces Round #442 (Div. 2) 877E - Danil and a Part-time Job dfs序+线段树
  5. 如何不做老板手中一次性筷子?
  6. 不会做抖音网红快闪PPT?这个插件可以免费一键生成快闪PPT!
  7. 组态服务器和客户端是啥协议,组态王服务器和客户端区别
  8. 网易云音乐的焦虑 暗藏在上市后的首份财报里
  9. PLSQLDeveloper下载使用
  10. 【数学】扩展欧几里得算法
  11. 数学模板-BSGSEXBSGS
  12. International Collegiate Programming Contest, Egyptian Collegiate Programming Contest (ECPC 2018)
  13. 【Unity优化篇】| Unity3D场景 常用优化策略,遮挡剔除、层消隐距离技术 和 LOD多层次细节
  14. apache ii评分怎么评_APACHE II评分表
  15. 《勋伯格和声学》读书笔记(一):大调三和弦的排列与结构
  16. 越来越多动物正在灭绝,“AI+动物”能否改变这一局面?
  17. 数码管点亮中几个常见三极管基极导通状态
  18. 【Linux】进程管理相关命令
  19. 华为S5720交换机通过ACL限制VLAN之间的访问
  20. elasticsearch 添加或修改分词器

热门文章

  1. Flask-SQLAlchemy - 不使用外键连表查询。记得常回来看我
  2. “康园圈--互联网+校园平台“项目之拓展手机客户端
  3. http请求与响应(content-type)
  4. 如何解决This system is not registered with RHN.
  5. img加载在IE11,chrome,FF下的不同
  6. gdb调试 -带有命令行参数
  7. 模板题——中国剩余定理,求组合数,卡特兰数
  8. 【GIS】投影类型的选择
  9. 解决eclispe SVN 创建资源库报错,无法验证:SVN…… 504 Connection to server timed out
  10. mysql日志打开_MySql 打开日志文件