每个月最头疼的事就是写教学月报。
领导要看的月报长这样:
以前我的流程:
- 打开12个班的Excel,把数据复制到一张总表里(半小时)
- 用Excel做折线图、柱状图,调整颜色、标题(一小时)
一上午就没了,而且每个月都要重复进行。
我决定让Python帮我干这活。折腾了两天,总算搞出一条流水线:从Excel到带图表的Word报告,一键生成,全程不用动手。
一、先看效果
我只需要在桌面上双击脚本,十几秒后,一个排版整齐、带彩色图表的Word月报就生成了,自动转成PDF,甚至能自动发到指定邮箱。
整个过程:各班Excel文件 → 数据汇总 → 生成图表 → 填入Word模板 → 转PDF → 发邮件
二、准备材料
1. 数据源:各班成绩Excel
每个班一个文件,比如 三(1)班.xlsx、三(2)班.xlsx……里面都有这几列:姓名、语文、数学、英语。
文件都放在 各班成绩 文件夹里。
2. Word模板:月报模板.docx
我在模板里留了几个占位符:
Word里插入图片的占位符比较麻烦,我直接留一个空段落,然后用代码在指定位置插入图片。
三、一步步写代码
第一步:读取所有Excel并汇总
import pandas as pdimport os文件夹 = '各班成绩'所有文件 = [f for f in os.listdir(文件夹) if f.endswith('.xlsx')]数据列表 = []for 文件名 in 所有文件: df = pd.read_excel(os.path.join(文件夹, 文件名)) df['班级'] = 文件名.replace('.xlsx', '') # 加一列班级名 数据列表.append(df)总表 = pd.concat(数据列表, ignore_index=True)# 计算总分、平均分总表['总分'] = 总表[['语文', '数学', '英语']].sum(axis=1)总表['平均分'] = (总表['总分'] / 3).round(1)# 按班级统计班级统计 = 总表.groupby('班级').agg({'语文': 'mean','数学': 'mean','英语': 'mean','总分': 'mean','姓名': 'count'}).round(1).rename(columns={'姓名': '人数'})
第二步:生成图表
import matplotlib.pyplot as plt# 设置中文字体(防止乱码)plt.rcParams['font.sans-serif'] = ['SimHei']plt.rcParams['axes.unicode_minus'] = False# 1. 近三个月成绩趋势(这里我用随机数据模拟,实际可以从历史数据读)近三月 = pd.DataFrame({'月份': ['3月', '4月', '5月'],'平均分': [85, 88, 92]})plt.figure(figsize=(8, 4))plt.plot(近三月['月份'], 近三月['平均分'], marker='o')plt.title('全校平均分趋势')plt.grid(True)plt.savefig('趋势图.png', dpi=150)plt.close()# 2. 各班平均分对比plt.figure(figsize=(10, 5))plt.bar(班级统计.index, 班级统计['总分'])plt.title('各班总分平均分对比')plt.xticks(rotation=45)plt.tight_layout()plt.savefig('对比图.png', dpi=150)plt.close()
第三步:用python-docx操作Word
from docx import Documentfrom docx.shared import Inches# 打开Word模板doc = Document('月报模板.docx')# 替换占位符文本(比如 {{分析文字}})for 段落 in doc.paragraphs:if'{{分析文字}}'in 段落.text: 段落.text = 段落.text.replace('{{分析文字}}', '本月整体成绩稳中有升,其中三(2)班进步明显,需关注三(1)班数学成绩。')# 插入汇总表(把DataFrame转成Word表格)# 先找到要插入表格的位置(比如在某个段落后面)for 段落 in doc.paragraphs:if'{{汇总表}}'in 段落.text:# 删除占位符段落 p = 段落._element p.getparent().remove(p)# 在相同位置插入表格 table = doc.add_table(rows=班级统计.shape[0]+1, cols=班级统计.shape[1]) table.style = 'Table Grid'# 添加表头for j, 列名 in enumerate(班级统计.columns): table.cell(0, j).text = 列名# 添加数据行for i, (index, row) in enumerate(班级统计.iterrows()): table.cell(i+1, 0).text = index # 班级名for j, 值 in enumerate(row): table.cell(i+1, j+1).text = str(值)break# 插入图片(趋势图)for 段落 in doc.paragraphs:if'{{趋势图}}'in 段落.text: 段落.text = 段落.text.replace('{{趋势图}}', '') 段落.add_run().add_picture('趋势图.png', width=Inches(6))breakfor 段落 in doc.paragraphs:if'{{对比图}}'in 段落.text: 段落.text = 段落.text.replace('{{对比图}}', '') 段落.add_run().add_picture('对比图.png', width=Inches(6))break# 保存Worddoc.save('教学月报.docx')print('✅ Word报告生成成功!')
第四步:转PDF
from docx2pdf import convertconvert('教学月报.docx', '教学月报.pdf')print('✅ PDF已生成')
第五步:自动发邮件
(第21天课程中讲过)
import smtplibfrom email.mime.multipart import MIMEMultipartfrom email.mime.text import MIMETextfrom email.mime.base import MIMEBasefrom email import encoders发件人 = '我的邮箱@qq.com'密码 = '授权码'收件人 = '领导邮箱@公司.com'邮件 = MIMEMultipart()邮件['From'] = 发件人邮件['To'] = 收件人邮件['Subject'] = '5月教学月报'正文 = MIMEText('领导好,5月教学月报请查收。', 'plain', 'utf-8')邮件.attach(正文)# 添加PDF附件with open('教学月报.pdf', 'rb') as f: 附件 = MIMEBase('application', 'octet-stream') 附件.set_payload(f.read()) encoders.encode_base64(附件) 附件.add_header('Content-Disposition', 'attachment; filename=教学月报.pdf') 邮件.attach(附件)服务器 = smtplib.SMTP_SSL('smtp.qq.com', 465)服务器.login(发件人, 密码)服务器.send_message(邮件)服务器.quit()print('✅ 邮件已发送')
四、遇到的坑和解决办法
一开始直接把图片插入到段落末尾,结果图片跑到下一页。后来用 add_run().add_picture() 在当前光标位置插入,并且删掉占位符段落,解决了。
table.style = 'Table Grid' 可以让表格有边框,看起来清爽。还可以用 doc.styles 自定义。
matplotlib 画图时一定要设置字体,否则图上的中文会变成方框。
模板里用不同的占位符 {{趋势图}} 和 {{对比图}},分别定位,互不干扰。
五、总结
现在每个月写月报,我只用双击脚本,去泡杯咖啡,回来报告就躺在桌面上了。领导再也没催过我。
Python自动化就是这样,把重复的劳动交给机器,把时间留给自己。
下期预告:第27天——毕业设计:做一个完整的“办公自动化工具箱”,把前面学的所有功能集成到一个图形界面里,让不懂代码的同事也能用。
回复「Py-Day」获取完整代码