17.Excel报表自动化(下):一键生成生产报表
一、问题背景领导要我每天改Excel格式小张这个报表的格式改一下。这句话我听了半年。每次领导有新想法我就要重新调整Excel排版。痛点1. 用MES导出的CSV数据手动复制到Excel模板2. 需要设置字体、颜色、边框、列宽3. 要加标题行、日期、图表4. 每周格式都会变用Python解决用openpyxl库直接在代码里定义格式一键生成。---二、技术原理openpyxl基础from openpyxl import Workbookfrom openpyxl.styles import Font, PatternFill, Alignment, Border# 创建工作簿wb Workbook()ws wb.activews.title 生产日报# 设置样式title_font Font(name微软雅黑, size14, boldTrue, colorFFFFFF)title_fill PatternFill(start_color1976D2, end_color1976D2, fill_typesolid)header_font Font(name微软雅黑, size11, boldTrue)center_align Alignment(horizontalcenter, verticalcenter)# 写入表头ws[A1] FAB生产日报ws[A1].font title_fontws[A1].fill title_fillws.merge_cells(A1:F1)# 设置列宽ws.column_dimensions[A].width 15---三、实战案例完整的Excel报表生成器FAB生产报表自动生成器功能自动化生成专业的Excel生产报表import pandas as pdimport numpy as npfrom openpyxl import Workbook, load_workbookfrom openpyxl.styles import Font, PatternFill, Alignment, Border, Sidefrom openpyxl.chart import BarChart, LineChart, Referencefrom openpyxl.utils import get_column_letterfrom datetime import datetime, timedeltafrom pathlib import Pathfrom typing import Dict, Optionalimport logginglogging.basicConfig(levellogging.INFO)logger logging.getLogger(__name__)class FABReportWriter:FAB Excel报表写入器def __init__(self):self.wb Workbook()# 颜色定义self.primary_color 1976D2self.warning_color FF9800self.danger_color F44336self.success_color 4CAF50self.light_gray F5F5F5# 字体定义self.title_font Font(name微软雅黑, size16, boldTrue, colorFFFFFF)self.header_font Font(name微软雅黑, size11, boldTrue, colorFFFFFF)self.data_font Font(name微软雅黑, size10)self.number_font Font(nameConsolas, size10)# 填充定义self.header_fill PatternFill(start_colorself.primary_color,end_colorself.primary_color, fill_typesolid)self.alt_fill PatternFill(start_colorself.light_gray,end_colorself.light_gray, fill_typesolid)self.title_fill PatternFill(start_color1565C0,end_color1565C0, fill_typesolid)# 对齐self.center_align Alignment(horizontalcenter, verticalcenter)self.left_align Alignment(horizontalleft, verticalcenter)# 边框thin_border Border(leftSide(stylethin, colorBDBDBD),rightSide(stylethin, colorBDBDBD),topSide(stylethin, colorBDBDBD),bottomSide(stylethin, colorBDBDBD),)self.cell_border thin_borderdef create_daily_report(self, data: pd.DataFrame,report_date: datetime,output_path: str) - str:创建日报参数:data: 生产数据report_date: 报告日期output_path: 输出路径ws self.wb.activews.title 生产日报date_str report_date.strftime(%Y年%m月%d日)# 标题行 ws.merge_cells(A1:H1)cell ws[A1]cell.value fFAB生产日报 - {date_str}cell.font self.title_fontcell.fill self.title_fillcell.alignment self.center_alignws.row_dimensions[1].height 35# 指标行 ws.merge_cells(A2:D2)ws.merge_cells(E2:H2)total_lots len(data)avg_yield data[yield_rate].mean() if yield_rate in data else 0ws[A2] f完成Lot: {total_lots}批ws[E2] f平均良率: {avg_yield:.1f}%ws[A2].font Font(name微软雅黑, size12, boldTrue)ws[E2].font Font(name微软雅黑, size12, boldTrue)# 数据表头 headers [Lot ID, 工序, Wafer数, 平均厚度(A), 厚度标准差,良率(%), 周期(分钟), 状态]for col, header in enumerate(headers, 1):cell ws.cell(row3, columncol, valueheader)cell.font self.header_fontcell.fill self.header_fillcell.alignment self.center_aligncell.border self.cell_border# 数据 for row_idx, (_, row) in enumerate(data.iterrows()):excel_row row_idx 4values [row.get(lot_id, ),row.get(process, ),row.get(wafer_count, 0),row.get(thickness_avg, 0),row.get(thickness_std, 0),row.get(yield_rate, 0),row.get(cycle_time, 0),正常 if row.get(yield_rate, 100) 90 else 异常,]for col, value in enumerate(values, 1):cell ws.cell(rowexcel_row, columncol, valuevalue)cell.font self.data_font if col 2 else self.number_fontcell.alignment self.center_aligncell.border self.cell_border# 交替行颜色if row_idx % 2 1:cell.fill self.alt_fill# 异常行标红if col 8 and value 异常:cell.font Font(name微软雅黑, size10, boldTrue,colorself.danger_color)# 自动调整列宽 for col in range(1, len(headers) 1):ws.column_dimensions[get_column_letter(col)].width 15# 保存 output_path Path(output_path)output_path.parent.mkdir(parentsTrue, exist_okTrue)self.wb.save(str(output_path))logger.info(f报表已生成: {output_path})return str(output_path)def add_charts(self, data: pd.DataFrame):添加图表ws self.wb.create_sheet(图表)# 图1工序产量柱状图chart BarChart()chart.type colchart.title 工序产量分布chart.y_axis.title Lot数chart.x_axis.title 工序chart.style 10# 工序统计process_stats data[process].value_counts()stats_data [[proc, count] for proc, count in process_stats.items()]# 写入临时数据for i, (proc, count) in enumerate(stats_data, 1):ws.cell(rowi, column1, valueproc)ws.cell(rowi, column2, valuecount)data_ref Reference(ws, min_col2, min_row1,max_rowlen(stats_data))cats_ref Reference(ws, min_col1, min_row1,max_rowlen(stats_data))chart.add_data(data_ref, titles_from_dataFalse)chart.set_categories(cats_ref)ws.add_chart(chart, D1)def create_weekly_report(self, weekly_data: pd.DataFrame,start_date: datetime,output_path: str) - str:创建周报ws self.wb.create_sheet(周报)start_str start_date.strftime(%m/%d)end_str (start_date timedelta(days6)).strftime(%m/%d)# 标题ws.merge_cells(A1:G1)ws[A1] fFAB周报 - {start_str}~{end_str}ws[A1].font self.title_fontws[A1].fill self.title_fillws[A1].alignment self.center_align# 日产量趋势daily_stats weekly_data.groupby(date).size().reset_index()daily_stats.columns [日期, 产量]for i, (_, row) in enumerate(daily_stats.iterrows()):ws.cell(row3, column1, valuerow[日期])ws.cell(row3, column2, valuerow[产量])self.wb.save(output_path)return output_path# 使用示例if __name__ __main__:# 模拟数据np.random.seed(42)n 50data pd.DataFrame({lot_id: [fFAB-{i:04d} for i in range(n)],process: np.random.choice([PHOTO, ETCH, CVD, CMP, IMP], n),wafer_count: np.random.randint(12, 26, n),thickness_avg: 1250 np.random.randn(n) * 3,thickness_std: 1 np.random.rand(n) * 2,yield_rate: np.clip(95 np.random.randn(n) * 3, 80, 100),cycle_time: np.random.randint(120, 360, n),})# 生成报表writer FABReportWriter()writer.create_daily_report(data,datetime.now(),reports/daily_report.xlsx)print(Excel报表已生成!)---四、效果对比维度 | 手工Excel | Python自动 | 提升|------|----------|-----------|------|生成时间 | 30分钟 | 5秒 | 360倍格式统一性 | 因人而异 | 完全统一 | 100%图表更新 | 手动拖数据 | 自动刷新 | 100%批量处理 | 困难逐个改 | 一键生成10份 | 10倍人工差错率 | 3%手滑填错 | 0.1%自动计算 | -97%可定制性 | 中Excel技能限制 | 高任意Python逻辑 | ∞我在项目中的效果把日报、周报、月报全部自动化后每周节省10小时。而且格式统一后领导再也不说这个表格式怎么跟上次不一样了。---五、实施建议Excel报表自动化三阶段第一阶段半自动化1-2天不要一上来就搞全自动。先用Python生成数据部分pandasopenpyxl写入数据格式调整合并单元格、颜色、字体暂时手工做。为什么先这样做因为你还不确定报表的最终格式自动化太早会浪费时间调整代码。先用半自动跑1-2周确认格式稳定了再完全自动化。第二阶段模板化2-3天确定格式后把样式封装到模板函数里# 定义常用样式HEADER_FILL PatternFill(start_color4472C4, fill_typesolid)HEADER_FONT Font(boldTrue, colorFFFFFF, size11)DATA_FONT Font(size10)THIN_BORDER Border(leftSide(stylethin), rightSide(stylethin),topSide(stylethin), bottomSide(stylethin))def apply_header_style(cell):cell.fill HEADER_FILLcell.font HEADER_FONTcell.alignment Alignment(horizontalcenter)cell.border THIN_BORDER这样定义一次样式所有报表复用格式100%统一。第三阶段完全自动化3-5天用定时任务Windows任务计划程序或Python schedule库每天自动生成报表并通过邮件发送给相关人员。踩坑提醒-openpyxl不能直接修改已有Excel中的图表只能创建新的。如果需要操作已有图表考虑用xlwings需要Excel安装在机器上-大Excel文件性能差超过10万行的Excelopenpyxl写入速度会明显下降。建议大数据量用CSV再用Excel打开-日期格式陷阱openpyxl的日期是Excel序列号如44927代表2023-01-01写入时需要用openpyxl.utils.datetime.to_excel()转换-合并单元格问题合并单元格后只有左上角的单元格有值读取时要注意处理---六、进阶方向1. xlwings - 操控Excel应用openpyxl只能读写文件xlwings可以直接操控正在运行的Excel程序——包括VBA宏、图表、数据透视表。适合需要与已有Excel模板深度交互的场景。import xlwings as xwwb xw.Book(template.xlsx)sheet wb.sheets[日报]sheet.range(A1).value today_data # 直接写入wb.save()2. Jinja2 Excel模板复杂报表可以用Jinja2模板引擎渲染Excel。模板中定义变量占位符{{ date }}Python填充数据后导出。适合报表格式经常变但结构不变的场景。3. PDF报告生成有些场景需要PDF格式的报表如给客户的质量报告可以用reportlab或weasyprint直接生成PDF绕过Excel。我们给客户的月度质量报告就是Python自动生成PDF省去了导出→打印→扫描→发邮件的繁琐流程。4. 数据看板如果日报只是给内部看可以考虑用Streamlit或Gradio做Web数据看板比Excel报表更直观。支持图表交互、数据筛选、实时刷新。--- 专栏VIP资源包包含本系列40篇全部可运行源码、示例数据集、自动化脚本工具包。在专栏主页点击「VIP资源」即可获取。---七、总结用openpyxl生成Excel报表关键是把格式化代码封装起来。一旦模板定义好了以后每次运行都一样。下一篇预告SQLite数据库操作——在本地管理FAB数据。--- 你平时做Excel报表最烦哪个环节有没有用过Python自动化欢迎评论区交流 专栏持续更新中关注不迷路。收藏点赞支持一下~ 专栏配套工具包含本篇完整可运行代码示例数据已上传为VIP资源专栏目录页可下载。