因为需要向税务局提交多份格式相同但内容有差别的报告,正好就以此作为项目练习VBA和Python代码。而且考虑到后续数据可能需要更新,如果到时候再依次打开各个文件并重新填写数据,肯定会花很多时间。编写代码其实就是为了利用其可以复用的特点,方便今后的维护。(当然,向税局提交报告后,今天写的代码应该就没有用了。)
完整代码:
github:zyeah007/excelToDoc
要完成的目标:
(1)根据信息表模板,创建20张新表,每张表以公司名字命名
(2)从包含有数据的工作表中获取单元格数据,并填写到对应公司的工作信息表中
(3)根据每家公司的信息表数据,生成对应的word版报告。报告表格中的数据与工作表对应单元格数据一致。
一、用VBA生成多张工作表
由于需要在Excel内部生成多张相同格式的表格,所以直接使用VBA更方便。
在VBA中复制多张工作表的代码为:
For i = 1 To 20 #共循环20次,创建20家参股公司信息表
Sheets("信息表模板").Copy after:=Sheets(Sheets.Count)
Sheets(Sheets.Count).name = infoSheet.Cells(2 + i, 2).Value #infoSheet为包含公司信息的工作表
Next
原有的信息表模板并不包含一个标题用于标明公司的名称,故需在每个生成的工作表中插入新行并输入公司的名称。
涉及到的操作包括:
(1)插入新行
(2)合并单元格
(3)输入公司名称(同工作表名)
for i =3 to Sheets.Count
maxCol = Sheets(i).columns.count # 注意工作表的索引表示方法Sheets(i)
Sheets(i).cells(1,1).EntireRow.Insert #插入行
Sheets(i).activate # 因后面有对单元格select动作,所以需要先激活工作表,否则会报错
Sheets(i).Range(Cells(1,2), Cells(1,maxCol)).Select
With Selection
.merge # 合并单元格
.HorizontalAlignment = xlCenter # 水平居中
.VerticalAlignment = xlCenter # 垂直居中
.Font.Bold = True # 单元格字体加粗
End With
.Cells(1,1).value = Sheets(i).name
Next
二、抓取数据并填充表格
这部分只是机械的编写给目标单元格赋值的表达式。而且,因为源单元格和目标单元格同处于excel内部,所以不用担心数据类型的问题。(这在后面编写python代码时确实让人头疼!)
唯一可以说的是对单元格的数据格式进行设置。这里需要设置某些单元格数据为百分比格式。
Sheets(corpName).cells(8, "D").numberformatlocal = "0.00%"
三、生成word版报告
这里其实有一个前提,即所谓的word版报告其实就是之前Excel信息表的word版。(更准确的讲,我是从word文档中复制了整个表格作为Excel文件的信息表模板的。)
首先,复制多个word文档,每个文档的文件名为公司名:
import shutil
shutil.copy(source, output)
读写Excel文件和word文件,分别要用到Python的openpyxl库和docx库。
import openpyxl
from docx import Document
打开一个公司的word文档,同时打开Excel文件,并根据该公司名称加载对应的Excel工作表
wb = openpyxl.load_workbook(workbook_path)
for file in os.listdir(file_path):
temp = file[14:]
corpName = temp.split('.')[0] # 获取公司名称
ws = wb[corpName]
doc = Document(os.path.join(file_path,file))
table = doc.tables[0] # 该word文档中只有一个表格
接下来又是一轮机械的编写赋值表达式。不过,与用VBA编写不同的是,这里是用Python读取Excel中的数据并以Python可接受的数据形式写入doc对象中,所以需要考虑数据类型的一致性。我在写代码的时候就遇到两个问题:
(1)从Excel单元格读取到的数据为空,需要转换为字符
(2)浮点数转换为百分比的输出格式
对于第一个问题,在赋值表达式中加一个判断语句
table.cell(1,9).text = infoSheet['K4'].value if infoSheet['K4'].value else ''
解释:如果某个单元格为空,则Python读取的结果为NoneType,它不能赋值给其他变量。所以加入该判断语句后,可以使得结果为一个空字符串。
对于第二个问题,采用格式输出法:
table.cell(5,2).text = '%.2f%%'%(infoSheet['D8'].value*100)
至此,基本完成了表格的生成、获取并填写数据、以及生成相应报告的任务。
四、总结
今天遇到并解决的问题包括:
1、在VBA中复制工作表,采用先复制后重命名的方式
2、在工作表中插入新行,合并单元格,并设置单元格格式
3、VBA中使用Select动作,需要先activate目标工作表,否则会报错
4、Python的docx模块中的Document对象,用于加载word文件。Document.tables属性,获取word文件中的表格。
5、Python的NoneType返回值不能用于赋值给变量
6、int和float型数据在转换为字符串用于输出时,可以使用字符串格式输出法,比如:'%.2f%%'%(float*100)