python案例实操_Python 操作 Word 案例

今天替老婆做一个小小的功能:

需求是这样的,她在银行对一个检查事项需要出具几份文档,例如《报告A》,《报告B》,《报告C》等。

但是这几份报告里面其实很多内容是重复的,例如人名,或者一些评价等等若干信息。

她希望可以不重复输入这些东西,而制作一些模板,来批量完成这项工作。这样对于大量的检查来说可以省掉不少工作量。

我隐约记得 word 里面有一插入域的功能,通过不同的 word 模板里面插入一些域作为钩子,然后引用到一个 access 文件,这样来进行批量的更新,但是由于其实我还是不太懂 word,于是浅尝辄止。

然后正好 python 的 office api 也是很快需要派上用场的,而且 python 都比较容易绿化所以干脆就写一个 python 的替换器吧。

简单的设想是这样的,做一个配置文件 conf.xlsx:

模板文件

输出目录

替换字段

替换内容

form1.docx

out

a

a for aster

form2.docx

b

b for bullets

c

c for ceasar

d

d for demon

稍微简单解释一下:

第一列是模板的文件,可以是若干个 word 文件,通过相对路径或者绝对路径指定。word 文件里面可以填写若干个 { {替换字段}} 这样的标签用于匹配替换。

第二列是输出目录,替换之后的文件将会被输出到指定的目录另存,也可以接受相对目录或者绝对路径的目录。

第三第四列是替换的字典,key 是第三列“替换字段”,value 第四列是替换内容。然后对于所有的模板文件中的 { {key}} 将会被全部替换成 value。

这样用起来已经可以省掉相当多的工作了,下面是实施这个脚本。

首先是找一个恰当的 python office api,以前没搞过,但是一下子就找到一个合适的了:

下载合适的版本进行安装,然后具体的使用可以 import win32com 这个包。

为了简化操作,我还在百度文库找到了一个简易版 word 封装:

# easyword.py

#!/usr/bin/env python

# -*- coding: utf-8 -*-

import win32com.client

import os

#--------------------------------------------------------------------------

class easyWord:

'''

Some convenience methods for Excel documents accessed

through COM.

'''

def __init__(self,visible=False):

self.wdApp = win32com.client.DispatchEx('Word.Application')

self.wdApp.Visible = visible

self.wdApp.DisplayAlerts = False

def new(self,filename=None):

'''

Create a new Word document. If 'filename' specified,

use the file as a template.

'''

if filename:

return self.wdApp.Documents.Add(filename)

else:

return self.wdApp.Documents.Add()

def open(self,filename):

'''

Open an existing Word document for editing.

'''

return self.wdApp.Documents.Open(filename)

def visible(self,visible=True):

self.wdApp.Visible = visible

def find(self,text,MatchWildcards=False):

'''

Find the string

'''

find = self.wdApp.Selection.Find

find.ClearFormatting()

find.Execute(text, False, False, MatchWildcards, False, False, True, 0)

return self.wdApp.Selection.Text

def replaceAll(self,oldStr,newStr):

'''

Find the oldStr and replace with the newStr.

'''

find = self.wdApp.Selection.Find

find.ClearFormatting()

find.Replacement.ClearFormatting()

find.Execute(oldStr, False, False, False, False, False, True, 1, True, newStr, 2)

def updateToc(self):

for tocitem in self.wdApp.ActiveDocument.TablesOfContents:

tocitem.Update()

def save(self):

'''

Save the active document

'''

self.wdApp.ActiveDocument.Save()

def saveAs(self,filename,delete_existing=True):

'''

Save the active document as a different filename.

If 'delete_existing' is specified and the file already

exists, it will be deleted before saving.

'''

if delete_existing and os.path.exists(filename):

os.remove(filename)

self.wdApp.ActiveDocument.SaveAs(FileName=filename)

def close(self):

'''

Close the active workbook.

'''

self.wdApp.ActiveDocument.Close()

def quit(self):

'''

Quit Word

'''

return self.wdApp.Quit()

这个作为一个模块,我们通过 from easyword import easyWord 即可导入这个类,这下就简单了。

然后实现一个方法 replaceWord:

# replace.py

import os

from easyword import easyWord

def replaceWord(data, tmpl_path=[], export_path=['export']):

for path in tmpl_path:

path = os.path.relpath(path)

if os.path.exists(path):

w = easyWord()

w.open(os.path.abspath(path))

print('打开:%s' % os.path.abspath(path))

for k in data:

w.replaceAll('{ {%s}}'%k, data[k])

for ex in export_path:

ex = os.path.join(os.path.abspath(ex), path)

if not os.path.exists(os.path.dirname(ex)):

os.makedirs(os.path.dirname(ex))

print('输出:%s' % ex)

w.saveAs(ex)

w.close()

w.quit()

然后我们调用 replaceWord 函数:

data 是替换的字典 {'替换字段': '替换内容', ...}

tmpl_path 是模板文件的列表(支持相对或者绝对路径)

export_path 是输出目录的路径

举个例子:

replaceWord(

{

'a':'a for apple',

'b':'b for bullets',

'c':'c for ceaser',

'd': 'd for damn',

'e': 'e for eclipse',

},

(

'form1.docx',

'form2.docx',

),

(

'a/a',

'b/b',

)

)

这样调用就可以实现一个批量操作。

好了,最后一步,我们需要从指定的 conf.xlsx 来读取这些参数并且按照参数执行这个过程。

同样,在百度轻易找到了一个建议封装的 Excel 类库:

# easyexcel.py

#!/usr/bin/env python

# -*- coding: utf-8 -*-

from win32com.client import Dispatch

import win32com.client

class easyExcel:

"""A utility to make it easier to get at Excel. Remembering

to save the data is your problem, as is error handling.

Operates on one workbook at a time."""

def __init__(self, filename=None):

self.xlApp = win32com.client.DispatchEx('Excel.Application')

if filename:

self.filename = filename

self.xlBook = self.xlApp.Workbooks.Open(filename)

else:

self.xlBook = self.xlApp.Workbooks.Add()

self.filename = ''

def save(self, newfilename=None):

if newfilename:

self.filename = newfilename

self.xlBook.SaveAs(newfilename)

else:

self.xlBook.Save()

def close(self):

self.xlBook.Close(SaveChanges=0)

del self.xlApp

def getCell(self, sheet, row, col):

"Get value of one cell"

sht = self.xlBook.Worksheets(sheet)

return sht.Cells(row, col).Value

def setCell(self, sheet, row, col, value):

"set value of one cell"

sht = self.xlBook.Worksheets(sheet)

sht.Cells(row, col).Value = value

def getRange(self, sheet, row1, col1, row2, col2):

"return a 2d array (i.e. tuple of tuples)"

sht = self.xlBook.Worksheets(sheet)

return sht.Range(sht.Cells(row1, col1), sht.Cells(row2, col2)).Value

def addPicture(self, sheet, pictureName, Left, Top, Width, Height):

"Insert a picture in sheet"

sht = self.xlBook.Worksheets(sheet)

sht.Shapes.AddPicture(pictureName, 1, 1, Left, Top, Width, Height)

def cpSheet(self, before):

"copy sheet"

shts = self.xlBook.Worksheets

shts(1).Copy(None,shts(1))

于是我们可以通过如下方式获取到配置(在 replace.py 的后面加上这一段代码,然后就可以直接跑出结果来了):

# replace.py

from easyexcel import easyExcel

x = easyExcel(os.path.abspath('conf.xlsx'))

tmpl_path = []

while x.getCell('sheet1', len(tmpl_path)+2, 1):

tmpl_path.append(x.getCell('sheet1', len(tmpl_path)+2, 1))

export_path = []

while x.getCell('sheet1', len(export_path)+2, 2):

export_path.append(x.getCell('sheet1', len(export_path)+2, 2))

if not export_path:

export_path = ['export']

data = {}

while x.getCell('sheet1', len(data)+2, 3):

data[x.getCell('sheet1', len(data)+2, 3)] = x.getCell('sheet1', len(data)+2, 4)

x.close()

replaceWord(data, tmpl_path, export_path)

【转载请附】愿以此功德,回向 >>

你可能感兴趣的:(python案例实操)