Python自动化办公 | 补写178份Word日报!

Python自动化办公 | 补写178份Word日报!
2020年09月29日 09:39 CSDN

作者|Ryoko

来源 | 凹凸数据ID:alltodata

|CSDN

不久前,一个同事有个项目要向领导交差,其中一部分工作是根据 excel 表中的每日数据,按格式整理成日报写入 word。好家伙!足足 178 天的量要补,如果要靠复制粘贴,岂不是肝到吐血,(你给我自己解决啊!)好吧ojbk,是时候祭出 Python 办公自动化了。

基础数据整理

首先让我们来看看数据样本和输出文档的需求(敏感数据已做和谐处理):原始 excel 文件中有 n 个子表,每个子表为一天的数据,存在无记录和有记录(部门数 ≥ 1,每个部门记录数 ≥ 1)两种情况,需分别整理成两种日报,一为纯文本描述,二为附带表格的文档。

撸起袖子,开骂!

哦不,开始写代码!

先将子表合成一个,便于统一观察每日数据记录的规律,也方便后期处理。使用 xlrd 库读表,获取工作簿中的活动表名,再使用 pandas 库遍历子表以合并,dataframe 格式的数据对 excel 表的相性绝佳。

defmerge_sheet(filepath):# 合并多个同表头的子表

wb = xlrd.open_workbook(filepath)

sheets = wb.sheet_names()

df_total = pd.DataFrame()

for name in sheets:

df = pd.read_excel(filepath, sheet_name=name)

df_total = df_total.append(df)

df_total.to_excel("merge.xlsx", index=False)

输出两种日报

1、纯文本文档

根据需要输出的日报样式,输出无记录的日报只需读取【日期】列和【填报部门】列,将【填报部门】列为无的日期段按每日输出即可。观察原表数据,直接筛选无填报记录的数据丢到命名为“无”的子表里。

这里也可以利用 .groupby() 对【填报部门】列分组,取“无”的那一组,可是要注意一点:虽然 Python 很强大,但不需要将所有事情都交给 Python 做。

导入库和模块如下:

import pandas as pd

import xlrd

from docx import Document

from docx.shared import Pt

from docx.shared import Inches

from docx.oxml.ns import qn

from docx.enum.text import WD_PARAGRAPH_ALIGNMENT

from docx.enum.section import WD_ORIENTATION

基本流程很简单,读入无填报记录的数据,按日期输出 word 文档。

defwu_to_word(filepath):

df = pd.read_excel(filepath, sheet_name="无")

date_list = list(df['日期'])

for d in date_list:

filename = wordname+str(d)+").docx"# 输出的word文件名

title = "("+str(d)[:4]+"."+str(d)[4:6]+"."+str(d)[6:8]+")"# 副标题日期XXXX.XX.XX

word = str(d)[:4]+"年"+str(d)[4:6]+"月"+str(d)[6:8]+"日"# 开头、落款日期XXXX年XX月XX日

wu_doc(title, word, filename)

print(f"文件:{filename},{title},{word} 已保存")

每份文档都会用到的同样的内容也可以先设定好。

wordname = "XX公司业务数据表(日报"all_title = " XX公司业务报告"

生成 word 内容,不加表格的情况下还是比较容易实现的,注意调整好格式。

defwu_doc(title,word,filename):# 传入副标题日期,文段开头及落款的日期,文件名

doc = Document() # 创建文档对象

section = doc.sections[] # 获取页面节点

section.orientation = WD_ORIENTATION.LANDSCAPE # 页面方向设为横版

new_width, new_height = section.page_height, section.page_width # 将原始长宽互换,实现将竖版页面变为横版

section.page_width = new_width

section.page_height = new_height

# 段落的全局设置

doc.styles['Normal'].font.name = u'宋体'# 字体

doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体') # 中文字体需再添加这个设置

doc.styles['Normal'].font.size = Pt(14) # 字号 四号对应14

t1 = doc.add_paragraph() # 添加一个段落

t1.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER # 居中

_t1 = t1.add_run(all_title) # 添加段落内容(大标题)

_t1.bold = True# 加粗

_t1.font.size = Pt(22)

t2 = doc.add_paragraph() # 再添加一个段落

t2.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER # 居中

_t2 = t2.add_run(title + "\n") # 添加段落内容(副标题)

_t2.bold = True

doc.add_paragraph(word + "无记录。\n\n").paragraph_format.first_line_indent = Inches(0.35) # 添加段落同时添加内容,并设置首行缩进

doc.add_paragraph(word).paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT # 落款日期右对齐

doc.save(dir+filename) # 按路径+文件名保存

执行!104 份无填报记录的日报就写好啦,干脆就这样交差吧,剩下的不想研究了哈哈哈。

2、附表格文档

有报送记录的数据处理起来相对复杂一点,先看一下原始数据。

比如 X 年 X 月 X 日,有 N 个部门填报了数据,根据文档样例,文段描述部分需要整理成如下格式:

部门 A:“报送内容1” X条记录;“报送内容2” Y条记录;部门 B:……;部门 C:……;

而附件表格部分需整理成如下格式,可以预想把每一行需要的数据整理一个 list,按行写入表格:

一级指标二级指标三级指标四级指标各部门报送情况备注
lalalahahahabalabala若为空则沿用上级部门A:报送内容1有记录未上传,没报,系统崩了
aaabbbcccddd部门A:报送内容2已上传,报的好
............部门B:报送内容1...

基本流程类似,读表后先按日期分组,每一组含一天中的一个或多个部门数据,再生成某一天的附件需要的表格,接着整理文段描述,最后按日期输出每一天的 word 文档。

defwhat_to_word(filepath):

df = pd.read_excel(filepath, sheet_name="有")

df.fillna('', inplace=True) # 替换nan值为空字符

dates = [] # 日期列表

df_total = [] # 分日期存的所有df

list_total = [] # 每一份word中需要的表数据合集

for d in df.groupby('日期'):

dates.append(d[])

df_total.append(d[1])

for index,date in enumerate(dates):

list_oneday = [] # 某一个word所需的表数据

for row in range(len(df_total[index])):

list_row = get_table_data(df_total, index, row) # 其中一行数据

list_oneday.append(list_row)

list_total.append(list_oneday)

for index, date in enumerate(dates):

filename = wordname+str(date)+").docx"# 输出的word文件名

title = "("+str(date)[:4]+"."+str(date)[4:6]+"."+str(date)[6:8]+")"# 副标题日期XXXX.XX.XX

word = str(date)[:4]+"年"+str(date)[4:6]+"月"+str(date)[6:8]+"日"# 开头、落款日期XXXX年XX月XX日

sentence = get_sentence(df_total, index) # 某一天的文段描述

what_doc(title, word, sentence, list_total[index], filename) #传入需要的内容后输出文档

print(f"文件:{filename} 已保存")

下面让我们分别看看整理表格、整理文段、输出文档是如何实现的。

整理表格

获取 excel 表中的一行数据(说明:df_total[df_index] 为一个 dataframe,其 values 为一个二维的 numpy 数组),整理各级指标、各部门报送情况和备注,返回一个列表。

defget_table_data(df_total, df_index, table_row):

list1 = df_total[df_index].values[table_row] # excel表中的一行

list2 = list1[3:7] # 一至四级指标

for i in range(len(list2)): # 当前指标为空则沿用上级指标

if list2[i] == '空'and i != :

list2[i] = list2[i - 1]

content = list1[2] + ":\n" + list1[-4] # 报送内容

if'否'in list1[-2]: # 备注

remark = '有记录未上传,' + str(list1[-1])

else:

remark = '已上传'

list3 = list2.tolist() # 需填入word中的表数据,由numpy数组转为list列表

list3.append(str(content))

list3.append(str(remark))

return list3

整理文段

对当日数据中的【填报部门】列中的唯一值计数,得知有 N 个部门填报了数据。对部门分组,获取其相关信息,组合成 [(报送内容,记录数,是否上报,备注)] 的格式,再整理出形如 “有N个部门报送了数据:部门X:“ 报送内容XXX ” X条记录;... ...” 的描述串。

defget_sentence(df_total, df_index):

df_oneday = df_total[df_index]

num = df_oneday['填报部门'].nunique() # 部门的数量

group = [] # 部门名称

detail = [] # 组合某个部门的数据,其中元素为元组格式(, , , )

info = ''# 报送情况描述

for item in df_oneday.groupby('填报部门'):

group.append(item[])

detail.append(

list(

zip(

list(item[1]['报送内容']),

list(item[1]['记录数']),

list(item[1]['是否上报']),

list(item[1]['备注'])

)

)

)

for index, g in enumerate(group): # 整理每个部门的填报情况

mes = str(g)+':'# 部门开头

for i in range(len(detail[index])):

_mes = detail[index][i]

if int(_mes[1])>:

mes = mes + f'“{_mes[]}{_mes[1]}条记录;'

info = info + mes

info = info[:-1]+"。"#将最后一个分号替换成句号

sentence = f"有{num}个部门报送了数据:{info}"

return sentence

出文档

(耐心警告!)调整 word 中的文本和表格样式的操作比较繁琐,需一步一步设置,预设表头如下:

table_title = ['一级指标', '二级指标', '三级指标', '四级指标', '各部门报送情况', '备注']

其他详见代码注释。

defwhat_doc(title, word, sentence, table, filename):# 传入副标题日期,开头/落款日期,文段,表数据,文件名

doc = Document()

section = doc.sections[]

new_width, new_height = section.page_height, section.page_width

section.orientation = WD_ORIENTATION.LANDSCAPE

section.page_width = new_width

section.page_height = new_height

# 段落的全局设置

doc.styles['Normal'].font.name = u'宋体'# 字体

doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体') # 中文字体需再添加这个设置

doc.styles['Normal'].font.size = Pt(14) # 字号 四号对应14

t1 = doc.add_paragraph() # 大标题

t1.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER # 居中

_t1 = t1.add_run(all_title)

_t1.bold = True

_t1.font.size = Pt(22)

t2 = doc.add_paragraph() # 副标题

t2.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER # 居中

_t2 = t2.add_run(title + "\n")

_t2.bold = True

doc.add_paragraph(word + sentence +"\n\n").paragraph_format.first_line_indent = Inches(0.35) # 首行缩进

doc.add_paragraph(word).paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT # 右对齐

doc.add_paragraph("各部门具体报送情况见附件:")

doc.add_page_break() # 分页---------------------------------------------------------------

fujian = doc.add_paragraph().add_run("\n附件")

fujian.bold = True

fujian.font.size = Pt(16)

t3 = doc.add_paragraph() # 附件大标题

t3.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER # 居中

_t3 = t3.add_run("XX公司业务数据表")

_t3.bold = True

_t3.font.size = Pt(22)

rows = len(table)+1

word_table = doc.add_table(rows=rows, cols=6, style='Table Grid') # 创建rows行、6列的表格

word_table.autofit=True# 添加框线

table = [table_title] + table # 固定的表头+表数据

for row in range(rows): # 写入表格

cells = word_table.rows[row].cells

for col in range(6):

cells[col].text = str(table[row][col])

for i in range(len(word_table.rows)): # 遍历行列,逐格修改样式

for j in range(len(word_table.columns)):

for par in word_table.cell(i, j).paragraphs: # 修改字号

for run in par.runs:

run.font.size = Pt(10.5)

for par in word_table.cell(, j).paragraphs: # 第一行加粗

for run in par.runs:

run.bold = True

doc.save(dir+filename)

执行!74份有记录的日报也写好啦,一共178份。

一顿操作猛如虎,总算是批量生成了日报,盒饭该加个鸡腿子了吧... ...

源码下载

如果大家对文中的源码和数据感兴趣,电脑网页打开下方链接即可下载

https://alltodata.cowtransfer.com/s/9c8f675d2f7544

财经自媒体联盟更多自媒体作者

新浪首页 语音播报 相关新闻 返回顶部