Day3:用AI + Python自动生成PPT周报/月报,再也不用对着空白页发呆
一、先说一个每周都在发生的场景
每到周五下午,你的日历上都有一条提醒:
「周报 PPT,今天 5 点前发给领导」
你打开 PowerPoint,看着空白页发呆。
数据在哪儿?在三个不同的 Excel 表里。
图表怎么做?得一个个手动插入。
格式怎么统一?去年的模板找半天。
你开始"复制-粘贴-调格式-复制-粘贴-调格式"……
一个半小时后,你提交了一份"差不多"的 PPT。
这不是做工作,这是在跟工具较劲。
或者是这样——
月底,老板让你出一份月度运营数据报告 PPT,要求:
数据有,但整理成 PPT 要花你整整半天。
今天,我们来让这件事自动完成。
二、PPT 自动化能做什么?
PPT 自动化的核心价值:把有规律的数据填充过程变成代码。
职场中最典型的 PPT 生成场景:
场景一:周报 / 月报自动生成
数据来自 Excel,自动生成包含图表、文字、数据的 PPT,每周/每月一键运行。
场景二:多部门数据汇报 PPT
一份数据表 → 按部门拆分 → 自动生成多份 PPT,每份只包含该部门的数据。
场景三:数据驱动的演示文稿
实时数据(销售、运营、财务)→ 自动更新 PPT 中的图表和数字,无需手动刷新。
今天我们重点解决:周报 / 月报的自动生成
三、核心工具:python-pptx
Python 中操作 PPT 的主要库是 python-pptx,可以:
安装命令:
pip install python-pptx openpyxl matplotlib
四、实战一:从Excel数据一键生成周报PPT
场景描述
你每周有一份销售数据 Excel(weekly_data.xlsx),包含:
你需要自动生成一份包含以下内容的 PPT:
先准备数据文件
假设 weekly_data.xlsx 结构如下:
完整脚本
# auto_ppt_report.py# 自动生成周报PPTimport jsonfrom datetime import datetime, timedeltafrom pptx import Presentationfrom pptx.util import Inches, Pt, Emufrom pptx.dml.color import RGBColorfrom pptx.enum.text import PP_ALIGNfrom pptx.chart.data import ChartDatafrom pptx.enum.chart import XL_CHART_TYPEimport openpyxlimport os# ============================================================# 配置区域 - 根据实际情况修改# ============================================================EXCEL_FILE = "weekly_data.xlsx" # 数据来源OUTPUT_FILE = "weekly_report.pptx" # 输出文件名WEEK_NUM = 15 # 第几周(可自动计算)DEPARTMENT = "销售部" # 部门名称# 品牌颜色COLOR_PRIMARY = RGBColor(0x1A, 0x73, 0xE8) # 蓝色(标题)COLOR_ACCENT = RGBColor(0xEA, 0x43, 0x35) # 红色(强调)COLOR_DARK = RGBColor(0x20, 0x21, 0x24) # 深色(正文)COLOR_LIGHT = RGBColor(0xF8, 0xF9, 0xFA) # 浅灰(背景)COLOR_SUCCESS = RGBColor(0x34, 0xA8, 0x53) # 绿色(达标)# ============================================================def read_excel_data(filepath): """读取Excel中的周报数据""" wb = openpyxl.load_workbook(filepath) ws = wb.active data = [] headers = [cell.value for cell in ws[1]] for row in ws.iter_rows(min_row=2, values_only=True): if row[0]: # 跳过空行 item = dict(zip(headers, row)) data.append(item) return datadef calc_summary(data): """计算汇总数据""" total_sales = sum(item.get("本周销售额", 0) or 0 for item in data) total_target = sum(item.get("目标销售额", 0) or 0 for item in data) completion_rate = (total_sales / total_target * 100) if total_target > 0 else 0 return { "total_sales": total_sales, "total_target": total_target, "completion_rate": round(completion_rate, 1), "best_product": max(data, key=lambda x: x.get("本周销售额", 0))["产品"], "best_channel_rate": max(data, key=lambda x: float(str(x.get("转化率", "0%")).replace("%", ""))) }def add_text_box(slide, text, left, top, width, height, font_size=18, bold=False, color=None, align=PP_ALIGN.LEFT): """往幻灯片上添加文本框(工具函数)""" txBox = slide.shapes.add_textbox( Inches(left), Inches(top), Inches(width), Inches(height)) tf = txBox.text_frame tf.word_wrap = True p = tf.paragraphs[0] p.alignment = align run = p.add_run() run.text = text run.font.size = Pt(font_size) run.font.bold = bold if color: run.font.color.rgb = color return txBoxdef create_cover_slide(prs, week_num, department): """创建封面页""" slide_layout = prs.slide_layouts[6] # 空白布局 slide = prs.slides.add_slide(slide_layout) # 背景色 - 深蓝渐变效果(用矩形模拟) from pptx.util import Inches bg = slide.shapes.add_shape( 1, # MSO_SHAPE_TYPE.RECTANGLE Inches(0), Inches(0), prs.slide_width, prs.slide_height ) bg.fill.solid() bg.fill.fore_color.rgb = RGBColor(0x0D, 0x47, 0xA1) bg.line.fill.background() # 装饰线 line = slide.shapes.add_shape( 1, Inches(0.8), Inches(2.8), Inches(1.5), Inches(0.06)) line.fill.solid() line.fill.fore_color.rgb = RGBColor(0x42, 0xA5, 0xF5) line.line.fill.background() # 标题 add_text_box(slide, f"2026年 第{week_num}周", 0.8, 1.5, 8, 0.8, font_size=20, color=RGBColor(0x90, 0xCA, 0xF9)) add_text_box(slide, f"{department}周报", 0.8, 2.1, 8, 1.2, font_size=40, bold=True, color=RGBColor(0xFF, 0xFF, 0xFF)) # 日期 today = datetime.now() week_start = today - timedelta(days=today.weekday()) week_end = week_start + timedelta(days=6) date_str = f"{week_start.strftime('%Y.%m.%d')} — {week_end.strftime('%Y.%m.%d')}" add_text_box(slide, date_str, 0.8, 3.4, 8, 0.5, font_size=16, color=RGBColor(0xBB, 0xDE, 0xFB)) # 生成时间 add_text_box(slide, f"生成时间:{today.strftime('%Y-%m-%d %H:%M')} | 自动生成报告", 0.8, 6.5, 8, 0.4, font_size=11, color=RGBColor(0x90, 0xCA, 0xF9)) return slidedef create_summary_slide(prs, summary): """创建数据概览页""" slide_layout = prs.slide_layouts[6] slide = prs.slides.add_slide(slide_layout) # 页面标题 add_text_box(slide, "本周核心数据", 0.5, 0.3, 9, 0.7, font_size=28, bold=True, color=COLOR_PRIMARY) add_text_box(slide, "Key Metrics Overview", 0.5, 0.95, 9, 0.4, font_size=13, color=RGBColor(0x80, 0x80, 0x80)) # 分隔线 line = slide.shapes.add_shape(1, Inches(0.5), Inches(1.3), Inches(9), Inches(0.03)) line.fill.solid() line.fill.fore_color.rgb = COLOR_PRIMARY line.line.fill.background() # 三个核心指标卡片 cards = [ { "label": "本周总销售额", "value": f"¥{summary['total_sales']:,.0f}", "sub": f"目标 ¥{summary['total_target']:,.0f}", "left": 0.4 }, { "label": "目标完成率", "value": f"{summary['completion_rate']}%", "sub": "↑ 超额完成" if summary['completion_rate'] >= 100 else "↓ 未达目标", "left": 3.5 }, { "label": "明星产品", "value": summary['best_product'], "sub": "销售额TOP1", "left": 6.6 } ] for card in cards: # 卡片背景 bg = slide.shapes.add_shape(1, Inches(card["left"]), Inches(1.5), Inches(2.8), Inches(3.2)) bg.fill.solid() bg.fill.fore_color.rgb = RGBColor(0xF0, 0xF4, 0xFF) bg.line.color.rgb = RGBColor(0xC5, 0xCF, 0xFF) add_text_box(slide, card["label"], card["left"] + 0.15, 1.65, 2.5, 0.5, font_size=13, color=RGBColor(0x60, 0x60, 0x60)) add_text_box(slide, card["value"], card["left"] + 0.1, 2.2, 2.6, 1.0, font_size=24, bold=True, color=COLOR_PRIMARY) add_text_box(slide, card["sub"], card["left"] + 0.15, 3.3, 2.5, 0.4, font_size=12, color=RGBColor(0x80, 0x80, 0x80)) return slidedef create_chart_slide(prs, data): """创建图表页(各产品销售额柱状图)""" slide_layout = prs.slide_layouts[6] slide = prs.slides.add_slide(slide_layout) add_text_box(slide, "各产品销售情况", 0.5, 0.3, 9, 0.7, font_size=28, bold=True, color=COLOR_PRIMARY) add_text_box(slide, "Product Sales Performance", 0.5, 0.95, 9, 0.4, font_size=13, color=RGBColor(0x80, 0x80, 0x80)) # 分隔线 line = slide.shapes.add_shape(1, Inches(0.5), Inches(1.3), Inches(9), Inches(0.03)) line.fill.solid() line.fill.fore_color.rgb = COLOR_PRIMARY line.line.fill.background() # 创建图表数据 chart_data = ChartData() chart_data.categories = [item["产品"] for item in data] chart_data.add_series("本周销售额", [item.get("本周销售额", 0) for item in data]) chart_data.add_series("目标销售额", [item.get("目标销售额", 0) for item in data]) # 插入柱状图 chart = slide.shapes.add_chart( XL_CHART_TYPE.COLUMN_CLUSTERED, Inches(0.5), Inches(1.5), Inches(9), Inches(4.5), chart_data ).chart # 图表样式优化 chart.has_legend = True chart.legend.position = 2 # 底部 chart.legend.include_in_layout = False # 设置系列颜色 series0 = chart.series[0] series0.format.fill.solid() series0.format.fill.fore_color.rgb = COLOR_PRIMARY series1 = chart.series[1] series1.format.fill.solid() series1.format.fill.fore_color.rgb = RGBColor(0xBB, 0xDE, 0xFB) return slidedef create_detail_table_slide(prs, data): """创建明细表格页""" slide_layout = prs.slide_layouts[6] slide = prs.slides.add_slide(slide_layout) add_text_box(slide, "明细数据", 0.5, 0.3, 9, 0.7, font_size=28, bold=True, color=COLOR_PRIMARY) add_text_box(slide, "Detailed Data", 0.5, 0.95, 9, 0.4, font_size=13, color=RGBColor(0x80, 0x80, 0x80)) # 分隔线 line = slide.shapes.add_shape(1, Inches(0.5), Inches(1.3), Inches(9), Inches(0.03)) line.fill.solid() line.fill.fore_color.rgb = COLOR_PRIMARY line.line.fill.background() # 插入表格 rows = len(data) + 1 cols = 4 table = slide.shapes.add_table( rows, cols, Inches(0.5), Inches(1.5), Inches(9), Inches(rows * 0.6) ).table # 设置列宽 table.columns[0].width = Inches(2.5) table.columns[1].width = Inches(2.2) table.columns[2].width = Inches(2.2) table.columns[3].width = Inches(2.1) # 表头 headers = ["产品名称", "本周销售额", "目标销售额", "完成率"] for i, h in enumerate(headers): cell = table.cell(0, i) cell.text = h cell.fill.solid() cell.fill.fore_color.rgb = COLOR_PRIMARY para = cell.text_frame.paragraphs[0] para.alignment = PP_ALIGN.CENTER run = para.runs[0] run.font.bold = True run.font.size = Pt(14) run.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF) # 数据行 for row_idx, item in enumerate(data): sales = item.get("本周销售额", 0) or 0 target = item.get("目标销售额", 1) or 1 rate = sales / target * 100 row_data = [ item.get("产品", ""), f"¥{sales:,.0f}", f"¥{target:,.0f}", f"{rate:.1f}%" ] for col_idx, val in enumerate(row_data): cell = table.cell(row_idx + 1, col_idx) cell.text = str(val) # 交替行背景色 if row_idx % 2 == 0: cell.fill.solid() cell.fill.fore_color.rgb = RGBColor(0xF0, 0xF4, 0xFF) para = cell.text_frame.paragraphs[0] para.alignment = PP_ALIGN.CENTER run = para.runs[0] run.font.size = Pt(13) # 完成率列:超额绿色,未达红色 if col_idx == 3: run.font.color.rgb = COLOR_SUCCESS if rate >= 100 else COLOR_ACCENT return slidedef create_conclusion_slide(prs, summary, week_num): """创建总结与展望页""" slide_layout = prs.slide_layouts[6] slide = prs.slides.add_slide(slide_layout) add_text_box(slide, "总结与下周计划", 0.5, 0.3, 9, 0.7, font_size=28, bold=True, color=COLOR_PRIMARY) add_text_box(slide, "Summary & Next Week Plan", 0.5, 0.95, 9, 0.4, font_size=13, color=RGBColor(0x80, 0x80, 0x80)) line = slide.shapes.add_shape(1, Inches(0.5), Inches(1.3), Inches(9), Inches(0.03)) line.fill.solid() line.fill.fore_color.rgb = COLOR_PRIMARY line.line.fill.background() # 本周总结 add_text_box(slide, "本周完成情况", 0.5, 1.5, 4.2, 0.5, font_size=16, bold=True, color=COLOR_DARK) status = "超额完成" if summary['completion_rate'] >= 100 else "未达目标,需改进" summary_text = ( f"本周总销售额达到 ¥{summary['total_sales']:,.0f}," f"目标完成率 {summary['completion_rate']}%,{status}。\n\n" f"明星产品 {summary['best_product']} 表现突出," f"建议下周持续加大该产品的推广力度。" ) add_text_box(slide, summary_text, 0.5, 2.0, 4.2, 2.5, font_size=13, color=COLOR_DARK) # 下周目标 add_text_box(slide, "下周重点目标", 5.2, 1.5, 4.2, 0.5, font_size=16, bold=True, color=COLOR_DARK) next_targets = [ f"1. 总销售额目标:¥{summary['total_target'] * 1.1:,.0f}(环比+10%)", f"2. 重点推进 {summary['best_product']} 系列产品", "3. 加强薄弱渠道的转化率提升", "4. 月底冲刺,确保季度目标达成" ] add_text_box(slide, "\n".join(next_targets), 5.2, 2.0, 4.2, 2.8, font_size=13, color=COLOR_DARK) # 底部署名 add_text_box(slide, f"第{week_num}周 | 本报告由Python脚本自动生成 | {datetime.now().strftime('%Y-%m-%d')}", 0.5, 6.8, 9, 0.4, font_size=11, color=RGBColor(0xA0, 0xA0, 0xA0), align=PP_ALIGN.CENTER) return slidedef generate_weekly_report(excel_file, output_file, week_num, department): """主函数:生成完整周报PPT""" print(f"正在读取数据: {excel_file}") # 检查Excel是否存在,不存在则生成示例数据 if not os.path.exists(excel_file): print("Excel文件不存在,生成示例数据...") wb = openpyxl.Workbook() ws = wb.active ws.append(["产品", "本周销售额", "目标销售额", "渠道", "转化率"]) ws.append(["产品A", 85000, 100000, "线上", "3.2%"]) ws.append(["产品B", 62000, 60000, "线下", "5.1%"]) ws.append(["产品C", 45000, 50000, "电商", "2.8%"]) ws.append(["产品D", 98000, 90000, "直播", "6.8%"]) wb.save(excel_file) print(f"已生成示例Excel: {excel_file}") data = read_excel_data(excel_file) summary = calc_summary(data) print(f"共读取 {len(data)} 条产品数据") print(f"总销售额: ¥{summary['total_sales']:,.0f},完成率: {summary['completion_rate']}%") # 创建演示文稿(16:9) prs = Presentation() prs.slide_width = Inches(10) prs.slide_height = Inches(7.5) print("正在生成各幻灯片...") create_cover_slide(prs, week_num, department) print(" [1/5] 封面页 ✓") create_summary_slide(prs, summary) print(" [2/5] 数据概览页 ✓") create_chart_slide(prs, data) print(" [3/5] 图表页 ✓") create_detail_table_slide(prs, data) print(" [4/5] 明细表格页 ✓") create_conclusion_slide(prs, summary, week_num) print(" [5/5] 总结展望页 ✓") prs.save(output_file) print(f"\n报告已生成: {output_file}") return output_fileif __name__ == "__main__": generate_weekly_report(EXCEL_FILE, OUTPUT_FILE, WEEK_NUM, DEPARTMENT) print("\n使用方法:") print("1. 修改脚本顶部的配置区域(Excel文件名、周次、部门等)") print("2. 运行: python auto_ppt_report.py") print("3. 查看生成的 weekly_report.pptx")
保存为 autopptreport.py,然后运行:
python auto_ppt_report.py
几秒钟,一份5页的专业周报 PPT 就生成好了。
五、实战二:批量为各部门生成专属月报
如果你需要给每个部门单独生成一份月报,只需在主函数上加一个循环:
# batch_monthly_report.py# 批量生成各部门月报import openpyxlfrom auto_ppt_report import generate_weekly_reportdef batch_generate(master_excel, output_dir): """读取总表,按部门拆分并批量生成PPT""" import os os.makedirs(output_dir, exist_ok=True) wb = openpyxl.load_workbook(master_excel) ws = wb.active # 读取所有数据 headers = [cell.value for cell in ws[1]] all_data = [] for row in ws.iter_rows(min_row=2, values_only=True): if row[0]: all_data.append(dict(zip(headers, row))) # 按部门分组 departments = {} for item in all_data: dept = item.get("部门", "未分类") departments.setdefault(dept, []).append(item) # 为每个部门生成PPT from datetime import datetime month = datetime.now().strftime("%Y年%m月") results = [] for dept, dept_data in departments.items(): # 临时保存部门数据到Excel temp_excel = f"{output_dir}/{dept}_temp.xlsx" wb_temp = openpyxl.Workbook() ws_temp = wb_temp.active ws_temp.append(list(dept_data[0].keys())) for row in dept_data: ws_temp.append(list(row.values())) wb_temp.save(temp_excel) output_pptx = f"{output_dir}/{dept}_{month}月报.pptx" generate_weekly_report(temp_excel, output_pptx, "月度", dept) os.remove(temp_excel) results.append(output_pptx) print(f"已生成: {output_pptx}") print(f"\n全部完成!共生成 {len(results)} 份月报") return resultsif __name__ == "__main__": batch_generate("company_data.xlsx", "monthly_reports")
六、进阶技巧:修改模板而不是从零开始
实际工作中,公司往往有固定的 PPT 模板(品牌色、Logo、标准格式)。
你不需要用代码重新制作这一切,而是直接修改现有模板中的占位符:
# use_template.py# 基于现有PPT模板填充数据from pptx import Presentationfrom pptx.util import Ptimport redef fill_ppt_template(template_path, output_path, data_dict): """ 在PPT模板中替换占位符 占位符格式:{{变量名}},例如 {{总销售额}} {{周次}} {{部门}} """ prs = Presentation(template_path) for slide in prs.slides: for shape in slide.shapes: if not shape.has_text_frame: continue for para in shape.text_frame.paragraphs: for run in para.runs: for key, value in data_dict.items(): placeholder = "{{" + key + "}}" if placeholder in run.text: run.text = run.text.replace(placeholder, str(value)) prs.save(output_path) print(f"已生成: {output_path}")# 示例用法if __name__ == "__main__": data = { "周次": "第15周", "部门": "销售部", "总销售额": "¥290,000", "完成率": "96.7%", "日期": "2026-04-10" } fill_ppt_template( template_path="company_template.pptx", # 你的公司模板 output_path="filled_report.pptx", data_dict=data )
使用步骤:
- 在需要动态替换的地方输入
{{变量名}},例如 {{总销售额}} - 保存为
company_template.pptx
七、AI 加速:让 AI 帮你写数据分析文字
你也许会说:"数字我有,但总结分析那段话不知道怎么写。"
这正是 AI 最擅长的事情。在脚本里加入 AI 分析模块:
# ai_analysis.py# 让AI自动生成数据分析文字import osdef get_ai_analysis(data_summary): """ 调用AI生成数据分析文字 这里以通义千问为例(也可以用ChatGPT、豆包等) """ prompt = f"""你是一名专业的数据分析师,请根据以下周报数据,写一段简洁的分析总结(150字以内):- 本周总销售额:¥{data_summary['total_sales']:,.0f}- 目标完成率:{data_summary['completion_rate']}%- 表现最好的产品:{data_summary['best_product']}要求:1. 指出本周业绩亮点2. 说明存在的不足3. 给出下周建议4. 语气专业简洁""" # 这里可以对接任意LLM API # 以下是伪代码,替换成你实际使用的API try: # 示例:调用本地Ollama import requests resp = requests.post("http://localhost:11434/api/generate", json={ "model": "qwen2.5", "prompt": prompt, "stream": False }) return resp.json().get("response", "AI分析生成失败") except Exception as e: return f"本周总销售额达 ¥{data_summary['total_sales']:,.0f},完成率 {data_summary['completion_rate']}%。整体表现{'优秀,继续保持' if data_summary['completion_rate'] >= 100 else '良好,仍有提升空间'}。"
八、总结与明日预告
今天我们系统学习了 PPT 自动化的核心技能:
学到了什么:
今天的核心收获:
明日预告:Day4——邮件通知自动化
有了 Excel 数据清洗、Word 批量生成、PPT 自动报告——下一步,如何把这些报告自动发送给相关人?
明天我们来搞定:Python 自动发送邮件,附件自动添加,收件人自动匹配。
别错过~
1. 安装 python-pptx,运行 autopptreport.py,看看生成的效果
2. 修改颜色配置(COLOR_PRIMARY 等),换成你公司的品牌色
3. 进阶:把你真实的周报数据放进去,生成一份真正可用的报告
系列文章:《AI办公自动化 7 天实战》
Day1 Excel批量清洗 ✅ | Day2 Word批量排版 ✅ | Day3 PPT自动生成 ✅ | Day4 邮件通知(明日)
— 量子位开发手记 —
AI办公自动化 7 天实战 · Day3
觉得有用,转发给你身边还在手动做PPT的同事 :)