Python-docx实战:同事要我帮忙补写178份日报!别吧
前言
大家好,这里是Seon塞翁。不久前,一个同事有个项目要向领导交差,其中一部分工作是根据 excel 表中的每日数据,按格式整理成日报写入 word。好家伙!足足 178 天的量要补,如果要靠复制粘贴,岂不是肝到吐血,(你给我自己解决啊!)好吧ojbk,是时候祭出 Python 办公自动化了。
文章目录
- 一、基础数据整理
- 二、输出两种日报
- (一)、纯文本文档
- (二)、附表格文档
- 1、整理表格
- 2、整理文段
- 3、输出文档
一、基础数据整理
首先让我们来看看数据样本和输出文档的需求(敏感数据已做和谐处理):原始 excel 文件中有 n 个子表,每个子表为一天的数据,存在无记录和有记录(部门数 ≥ 1,每个部门记录数 ≥ 1)两种情况,需分别整理成两种日报,一为纯文本描述,二为附带表格的文档。
撸起袖子,开骂!哦不,开始写代码!
先将子表合成一个,便于统一观察每日数据记录的规律,也方便后期处理。使用 xlrd 库读表,获取工作簿中的活动表名,再使用 pandas 库遍历子表以合并,dataframe 格式的数据对 excel 表的相性绝佳。
def merge_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)
二、输出两种日报
(一)、纯文本文档
根据需要输出的日报样式,输出无记录的日报只需读取【日期】列和【填报部门】列,将【填报部门】列为无的日期段按每日输出即可。观察原表数据,直接筛选无填报记录的数据丢到命名为“无”的子表里。
这里也可以利用 .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 文档。
def wu_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.XXword = 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 内容,不加表格的情况下还是比较容易实现的,注意调整好格式。
def wu_doc(title,word,filename): # 传入副标题日期,文段开头及落款的日期,文件名doc = Document() # 创建文档对象section = doc.sections[0] # 获取页面节点
section.orientation = WD_ORIENTATION.LANDSCAPE # 页面方向设为横版
new_width, new_height = section.page_height, section.page_width # 将原始长宽互换,实现将竖版页面变为横版section.page_width = new_widthsection.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) # 字号 四号对应14t1 = 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 = Truedoc.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 份无填报记录的日报就写好啦,干脆就这样交差吧,剩下的不想研究了哈哈哈。
(二)、附表格文档
有报送记录的数据处理起来相对复杂一点,先看一下原始数据。
比如 X 年 X 月 X 日,有 N 个部门填报了数据,根据文档样例,文段描述部分需要整理成如下格式:
部门 A:“报送内容1” X条记录;“报送内容2” Y条记录;部门 B:……;部门 C:……;
而附件表格部分需整理成如下格式,可以预想把每一行需要的数据整理一个 list,按行写入表格:
一级指标 | 二级指标 | 三级指标 | 四级指标 | 各部门报送情况 | 备注 |
---|---|---|---|---|---|
lalala | hahaha | balabala | 若为空则沿用上级 | 部门A:报送内容1 | 有记录未上传,没报,系统崩了 |
aaa | bbb | ccc | ddd | 部门A:报送内容2 | 已上传,报的好 |
… | … | … | … | 部门B:报送内容1 | … |
基本流程类似,读表后先按日期分组,每一组含一天中的一个或多个部门数据,再生成某一天的附件需要的表格,接着整理文段描述,最后按日期输出每一天的 word 文档。
def what_to_word(filepath):df = pd.read_excel(filepath, sheet_name="有")df.fillna('', inplace=True) # 替换nan值为空字符dates = [] # 日期列表df_total = [] # 分日期存的所有dflist_total = [] # 每一份word中需要的表数据合集for d in df.groupby('日期'):dates.append(d[0])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.XXword = 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} 已保存")
下面让我们分别看看整理表格、整理文段、输出文档是如何实现的。
1、整理表格
获取 excel 表中的一行数据(说明:df_total[df_index]
为一个 dataframe,其 values
为一个二维的 numpy 数组),整理各级指标、各部门报送情况和备注,返回一个列表。
def get_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 != 0: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
2、整理文段
对当日数据中的【填报部门】列中的唯一值计数,得知有 N 个部门填报了数据。对部门分组,获取其相关信息,组合成 [(报送内容,记录数,是否上报,备注)]
的格式,再整理出形如 “有N个部门报送了数据:部门X:“ 报送内容XXX ” X条记录;… …” 的描述串。
def get_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[0])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])>0:mes = mes + f'“{_mes[0]}”{_mes[1]}条记录;'info = info + mesinfo = info[:-1]+"。" #将最后一个分号替换成句号sentence = f"有{num}个部门报送了数据:{info}"return sentence
3、输出文档
(耐心警告!)调整 word 中的文本和表格样式的操作比较繁琐,需一步一步设置,预设表头如下:
table_title = [‘一级指标’, ‘二级指标’, ‘三级指标’, ‘四级指标’, ‘各部门报送情况’, ‘备注’]
其他详见代码注释。
def what_doc(title, word, sentence, table, filename): # 传入副标题日期,开头/落款日期,文段,表数据,文件名doc = Document()section = doc.sections[0]new_width, new_height = section.page_height, section.page_widthsection.orientation = WD_ORIENTATION.LANDSCAPEsection.page_width = new_widthsection.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) # 字号 四号对应14t1 = 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 = Truedoc.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 = Truefujian.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)+1word_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].cellsfor 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(0, j).paragraphs: # 第一行加粗for run in par.runs:run.bold = Truedoc.save(dir+filename)
执行!74份有记录的日报也写好啦,一共178份。
一顿操作猛如虎,总算是批量生成了日报,盒饭该加个鸡腿子了吧… …
Python-docx实战:同事要我帮忙补写178份日报!别吧相关推荐
- Python自动化办公 | 补写178份Word日报!
作者 | Ryoko 来源 | 凹凸数据(ID:alltodata) 头图 | CSDN 下载自视觉中国 不久前,一个同事有个项目要向领导交差,其中一部分工作是根据 excel 表中的每日数据,按格 ...
- 《python数据分析实战》第七章手写
以后建模希望可以直接用到,做个备忘 #%% import numpy as np from sklearn import datasets,linear_model,svm,neighbors,tre ...
- Python爬虫实战六之抓取爱问知识人问题并保存至数据库
大家好,本次为大家带来的是抓取爱问知识人的问题并将问题和答案保存到数据库的方法,涉及的内容包括: Urllib的用法及异常处理 Beautiful Soup的简单应用 MySQLdb的基础用法 正则表 ...
- selenium2 python自动化测试实战(回归测试)
selenium2 python自动化测试实战 最近接手商城的项目,针对后台测试,功能比较简单,但是流程比较繁多,涉及到前后台的交叉测试.在对整个项目进行第一轮测试完成之后,考虑以后回归测试任务比较重 ...
- qq纵横四海源码_【0基础】纵横中文网python爬虫实战
原文在此~ [0基础]纵横中文网python爬虫实战mp.weixin.qq.com 大家好,我是你们的机房老哥! 在粉丝群的日常交流中,爬虫是比较常见的话题.python最强大的功能之一也是爬虫. ...
- 我妈给我介绍对象了,我大学还没毕业呢,先在婚介市场也这么卷了的吗?【Python爬虫实战:甜蜜蜜婚介数据采集】
大家好,我是辣条. 说出来你们可能不信,我一个在校还没毕业的学生家里竟然给我介绍对象了-这么着急的吗?现在结婚市场都这么卷了吗?男孩们女孩们不努力的话是会被家里捉回去结婚的哦. 这是和我妈的聊天对话, ...
- python爬虫——实战篇
python爬虫--实战篇 2021.7.20晚已更新 注:注释和说明已在代码中注释 python爬虫实战篇 笔趣阁小说及其网址爬取 4k图片网站图片爬取 简历模板爬取 自动填体温小程序 待补充 笔趣 ...
- python项目实战——银行取款机系统(一)
项目实战目录 python项目实战--银行取款机系统(一) 前言 今天我们将通过python完成简易银行提款机系统的实战,我们一步步实现我们的要求.话不多说,看操作. 环境使用 python 3.9 ...
- Python办公自动化实战 01 | Python-docx库:Python与Word的完美结合1
本文参考(12条消息) Python办公自动化实战 04 | Python-docx库:Python与Word的完美结合_综合案例快速上手_跟风舞烟学编程的博客-CSDN博客_python-docx库 ...
最新文章
- [3]工欲善其事必先利其器-------UML常用的图(三)
- jackson 问题定位
- 内联函数的定义可能不止一次
- openstack的vnc启动ssl
- 数据结构和算法的基本概念
- Java实现动态加载页面_[Java教程]动态加载页面数据的小工具 javascript + jQuery (持续更新)...
- [转]js中escape,encodeURI,encodeURIComponent三个函数的区别
- 2021年杭州电子科技计算机考研大纲,杭州电子科技大学2021年硕士研究生招生专业目录与自命题科目考试大纲...
- 周长相等的正方形面积一定相等_周长和面积一直是三年级孩子的易失分点,家长要把好关!...
- 【课程设计】图书管理系统 C语言版---操作系统版
- ati 缺少关键性文件_ATI显卡优化
- 2020-06-10
- NOIp2014 提高组 Day1 T1 生活大爆炸版石头剪刀布
- 诺,你们要的Python进阶来咯!我还没见过比这全面的!
- 火影_青鸟_中日罗马音
- HTML+CSS 仿写京东网站界面
- NYoj 239 :月老的难题(二分图最大匹配)
- 手机4k屏幕测试软件,索尼Z5 Premium 4K屏幕测试:1080p屏是王道!
- 程序语言 | 编程范式/泛型一览
- 数据安全--1--专栏开篇作-数据安全概念及架构