Python 读取 Excel 并写入 zip:自动化报表打包实战

发布时间:2026/10/14 17:36:17

Python 读取 Excel 并写入 zip:自动化报表打包实战 简介这份资源面向需要处理表格数据的Python学习者与开发者聚焦Excel文件的读取与写入这一常见任务帮助解决数据分析、数据清洗与自动化报告中的表格操作问题。压缩包共4个文件全部为py脚本整体约6KB涵盖读取、按条件筛选、写入以及读写结合等不同操作示例便于按需查阅与直接运行。内容围绕pandas与openpyxl两个常用库展开pandas部分演示read_excel读取、条件筛选及to_excel写入openpyxl部分则涉及工作簿加载、单元格遍历与新建保存可帮助读者理解两种方案在简单表格与复杂格式场景下的差异。目前已有702人学习适合希望快速上手Excel自动化处理、积累脚本模板的初中级Python使用者参考。1. 用 Python 读取 Excel 并写入 zip一个被低估的自动化刚需财务同事每周五下午都要把 12 个区域的销售明细表汇总再打包成一个压缩包发给总部。手动操作是打开每个 xlsx复制粘贴到总表另存为右键压缩。一套流程 40 分钟错一个单元格就得重来。这种场景下用 Python 读取 Excel 并写入 zip 不是炫技而是把重复劳动压缩成一条命令的刚需。这个标题拆开看是两件事读 Excelpandas/openpyxl 负责和写 zip标准库 zipfile 负责。难点不在单个库怎么用而在两者衔接时的内存管理、路径处理和编码问题。适合有基础 Python 语法、日常要处理报表打包的运营、财务、数据分析从业者。下面按「读进来 → 处理好 → 压出去」的链路把每一步的参数和坑讲透。2. 读取 Excel 的三种姿势pandas、openpyxl 与 calamine 怎么选2.1 先搞清楚你的 Excel 到底长什么样选库之前先确认三件事文件是 .xlsx 还是 .xls、有没有合并单元格、单表数据量多大。.xls 是旧二进制格式pandas 需要 xlrd 才能读而新版 xlrd 已经放弃了对 xlsx 的支持这个历史包袱经常让人翻车。合并单元格在 pandas 里读出来是 NaN需要额外填充。数据量超过 10 万行时pandas 默认的全量加载会吃掉大量内存。我一般先用一条命令探底python -c import pandas as pd; df pd.read_excel(sample.xlsx, nrows5); print(df.shape); print(df.columns.tolist())nrows5只读前 5 行快速看列名和结构避免大文件全量加载。df.columns.tolist()打印列名列表确认表头在第几行——很多报表前两行是标题和空行直接读会把标题当列名。2.2 pandas 读取的核心参数与适用边界pandas 的read_excel是最省事的选择适合结构规整、单表不超过几十万行的场景。关键参数有四个import pandas as pd df pd.read_excel( sales_2024.xlsx, sheet_nameSheet1, # 指定工作表None 表示读全部 header0, # 表头所在行从 0 开始计数 usecolsA:F, # 只读 A 到 F 列减少内存 dtype{订单号: str}, # 强制订单号为字符串防止前导零丢失 engineopenpyxl # 显式指定引擎 ) print(df.head())sheet_name传字符串读单表传None返回一个字典键是表名、值是 DataFrame适合批量处理多 sheet。usecols用列字母或列名列表限定范围能显著降低内存。dtype是血泪经验订单号、身份证号这类字段如果被 pandas 推断成整数前导零会消失必须显式声明为 str。engine在 xlsx 场景下用 openpyxlxls 场景用 xlrd不指定时 pandas 会自动选但自动选偶尔会选错。读进来的 DataFrame 可以直接做筛选、合并、计算处理完再决定怎么写出去。2.3 openpyxl 直接操作单元格需要保留格式时的选择pandas 读进来的是纯数据原表的字体、颜色、公式全部丢失。如果压缩包里的 Excel 需要保留格式就得用 openpyxl 直接操作工作簿对象from openpyxl import load_workbook wb load_workbook(template.xlsx, data_onlyTrue) ws wb[Sheet1] # 遍历所有行跳过表头 for row in ws.iter_rows(min_row2, values_onlyTrue): order_id, amount, region row[0], row[1], row[2] if amount and amount 10000: print(f大额订单: {order_id} 金额 {amount} 区域 {region}) wb.save(filtered.xlsx)data_onlyTrue表示读取公式的计算结果而不是公式本身这个参数在读取别人发来的报表时特别重要否则你拿到的是SUM(A1:A10)这样的字符串。iter_rows的values_onlyTrue直接返回值元组比逐个访问cell.value快很多。openpyxl 的代价是速度慢10 万行以上会明显卡顿适合小批量、重格式的场景。2.4 大文件场景calamine 引擎的提速效果数据量到几十万行时pandas 默认的 openpyxl 引擎会慢到让人怀疑人生。python-calamine 是一个用 Rust 写的读取引擎pandas 2.2 之后可以直接通过enginecalamine调用import pandas as pd df pd.read_excel( big_data.xlsx, enginecalamine, usecols[日期, 金额, 区域] ) print(f读取完成共 {len(df)} 行)实测在 50 万行、20 列的 xlsx 上calamine 比 openpyxl 快 3 到 5 倍内存占用也更低。代价是需要额外安装python-calamine包且对某些冷门格式支持不如 openpyxl 完善。我的习惯是小文件用 openpyxl大文件先试 calamine遇到格式报错再退回 openpyxl。3. 把处理结果写进 zipzipfile 的四种写入模式3.1 从内存直接写入避免落盘的 BytesIO 方案最直觉的做法是先把 DataFrame 存成 xlsx 文件再压缩。但中间文件多了之后清理是个麻烦。更好的方式是用BytesIO在内存里完成 Excel 的序列化直接写进 zipimport pandas as pd from io import BytesIO from zipfile import ZipFile, ZIP_DEFLATED df pd.read_excel(source.xlsx) # 在内存中生成 xlsx buffer BytesIO() with pd.ExcelWriter(buffer, engineopenpyxl) as writer: df.to_excel(writer, indexFalse, sheet_name汇总) buffer.seek(0) # 关键把指针拨回开头 # 写入 zip with ZipFile(output.zip, w, compressionZIP_DEFLATED) as zf: zf.writestr(汇总表.xlsx, buffer.read()) print(打包完成)BytesIO是一个内存中的二进制流ExcelWriter把 DataFrame 写进去之后指针停在末尾必须seek(0)拨回开头否则read()读到的是空内容——这个坑我踩过不止一次。writestr的第一个参数是压缩包内的文件名可以带路径如2024/汇总表.xlsxzipfile 会自动创建目录结构。ZIP_DEFLATED是压缩算法对 xlsx 这种本身已压缩的格式压缩率有限但聊胜于无。3.2 多文件批量打包循环写入与命名规范实际场景往往是把多个处理结果打进一个包。用循环配合writestr即可import pandas as pd from io import BytesIO from zipfile import ZipFile, ZIP_DEFLATED regions [华东, 华南, 华北, 西南] source pd.read_excel(all_sales.xlsx) with ZipFile(regional_reports.zip, w, compressionZIP_DEFLATED) as zf: for region in regions: subset source[source[区域] region] if subset.empty: continue # 空数据跳过避免生成空文件 buf BytesIO() with pd.ExcelWriter(buf, engineopenpyxl) as writer: subset.to_excel(writer, indexFalse, sheet_nameregion) buf.seek(0) zf.writestr(f{region}_销售明细.xlsx, buf.read()) print(f{region}: {len(subset)} 行已写入)subset.empty判断很关键某些区域可能没有数据生成一个空 xlsx 既占空间又让接收方困惑。文件名用 f-string 拼接区域名保证压缩包内文件可读。循环内每次新建BytesIO不要复用同一个 buffer否则会读到上一次的残留数据。3.3 压缩级别与文件名的三个细节ZipFile的compresslevel参数控制压缩级别取值 0 到 9默认是 None相当于 6from zipfile import ZipFile, ZIP_DEFLATED # 高压缩适合归档 with ZipFile(archive.zip, w, compressionZIP_DEFLATED, compresslevel9) as zf: zf.write(report.xlsx, arcname2024年度报告.xlsx)compresslevel9压缩率最高但耗时最长compresslevel1最快但文件大。对 xlsx 来说因为内部已经是 zip 格式再压缩收益很小我一般用默认值。arcname参数指定文件在压缩包内的名字不指定则用原路径容易带上一长串本地目录。中文文件名在 zipfile 里默认用 UTF-8 编码Windows 自带解压工具可能显示乱码接收方用 7-Zip 或 Bandizip 打开正常。3.4 追加与更新a 模式与重复写入的陷阱ZipFile的a模式可以在已有压缩包上追加文件from zipfile import ZipFile, ZIP_DEFLATED with ZipFile(output.zip, a, compressionZIP_DEFLATED) as zf: zf.write(new_report.xlsx, arcname补充报表.xlsx)注意如果压缩包里已经存在同名文件追加不会覆盖而是出现两个同名条目解压时后者覆盖前者行为取决于解压工具。要更新文件必须先读出全部内容、删掉旧条目、重新写入或者干脆重建整个 zip。我一般不在原包上追加而是每次生成新包避免这种玄学问题。4. 避坑指南编码、内存与路径的五个真实翻车现场4.1 中文文件名在 Windows 解压后变乱码现象Python 生成的 zip 在 Windows 资源管理器里解压中文文件名显示为乱码用 7-Zip 打开却正常。原因是 zipfile 默认用 UTF-8 编码文件名而 Windows 自带解压工具按本地代码页解析。解决方式是在写入时显式指定编码或者干脆用英文文件名加一个中文映射表。我的做法是压缩包内文件名用拼音或英文在包内附一个manifest.txt说明对应关系接收方一看就懂。4.2 BytesIO 忘记 seek(0) 导致写入空文件现象压缩包生成了但里面的 xlsx 打开是空白。原因就是前面提到的ExcelWriter写完后指针在末尾read()返回空字节。解决每次read()之前调用buffer.seek(0)。这个错误不报异常只是静默生成空文件排查起来很费时间。养成习惯凡是 BytesIO 读写切换先 seek。4.3 大文件全量加载导致内存溢出现象处理 100 万行的 xlsx 时进程被系统杀掉。原因是 pandas 默认全量加载100 万行 30 列大约占用 2 到 3 GB 内存。解决用read_excel的usecols限定列用chunksize分块读取注意 read_excel 的 chunksize 返回的是迭代器或者换 calamine 引擎降低内存占用。如果只是筛选后打包根本不需要全量加载分块处理完直接写 zip 即可。4.4 日期列读出来变成数字现象Excel 里的日期2024-01-15读进来变成45306。原因是 Excel 内部用序列号存储日期openpyxl 在某些配置下不自动转换。解决读取后用pd.to_datetime(df[日期], unitD, origin1899-12-30)转换或者在read_excel里指定parse_dates[日期]。注意 origin 是 1899-12-30 而不是 1900-01-01因为 Excel 有一个著名的 1900 闰年 bug。4.5 压缩包内路径用了反斜杠现象在 Windows 上开发压缩包内文件名写成reports\\summary.xlsx在 Linux 上解压变成一个名字里带反斜杠的文件。原因是 zip 规范要求路径分隔符用正斜杠/。解决统一用os.path.join或 pathlib 生成路径后把反斜杠替换成正斜杠或者直接用 f-string 写reports/summary.xlsx。跨平台代码里路径拼接永远用正斜杠。5. 进阶技巧用上下文管理器封装一个可复用的打包函数把前面的逻辑封装成一个函数是让这套方案真正可复用的关键。下面这个函数接收一个 Excel 路径和分组列名按分组拆表并打包全程内存操作不落中间文件import pandas as pd from io import BytesIO from zipfile import ZipFile, ZIP_DEFLATED from pathlib import Path def excel_to_zip_by_group( excel_path: str, group_col: str, output_zip: str, sheet_name: str Sheet1, compress_level: int 6 ) - int: 按指定列分组将每组数据写入独立 xlsx 并打包成 zip。 返回写入的文件数量。 df pd.read_excel(excel_path, sheet_namesheet_name) if group_col not in df.columns: raise ValueError(f列 {group_col} 不存在实际列: {df.columns.tolist()}) count 0 with ZipFile(output_zip, w, compressionZIP_DEFLATED, compresslevelcompress_level) as zf: for name, group in df.groupby(group_col): buf BytesIO() with pd.ExcelWriter(buf, engineopenpyxl) as writer: group.to_excel(writer, indexFalse, sheet_namestr(name)[:31]) buf.seek(0) safe_name str(name).replace(/, _).replace(\\, _) zf.writestr(f{safe_name}.xlsx, buf.read()) count 1 return count if __name__ __main__: n excel_to_zip_by_group(all_sales.xlsx, 区域, regional.zip) print(f共打包 {n} 个文件)几个设计细节值得说。sheet_namestr(name)[:31]是因为 Excel 工作表名上限 31 个字符超长会报错。safe_name把分组名里的斜杠替换掉防止路径穿越或非法文件名。groupby默认会排除 NaN 分组如果分组列有空值这些行会被静默丢弃需要提前用fillna(未知)处理。返回文件数量方便调用方做校验。验证方法很简单生成 zip 后用ZipFile.namelist()打印条目列表再逐个read()出来用 pandas 读一遍确认行数和原数据一致from zipfile import ZipFile import pandas as pd from io import BytesIO with ZipFile(regional.zip) as zf: for name in zf.namelist(): data pd.read_excel(BytesIO(zf.read(name))) print(f{name}: {len(data)} 行, 列: {data.columns.tolist()})这套流程我用了两年多最大的教训是不要相信 Excel 的表头一定在第一行也不要想当然认为分组列没有空值。每次处理新报表先跑一遍探查命令确认结构再写正式逻辑。另一个习惯是压缩包内永远附一个manifest.txt记录生成时间、数据行数和分组依据接收方出问题时能快速定位。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/14 17:36:17

九轴姿态解算实战:C语言实现Mahony互补滤波与四元数融合

简介:九轴姿态解算源代码是一套面向嵌入式开发、无人机及机器人等场景的C语言实现,融合加速度计、磁力计与陀螺仪数据,用于实时估计物体的横滚、俯仰与偏航角。资源包含4个文件,全部为2个C源文件和2个头文件,压缩包仅5…

2026/10/14 17:31:07

QSV硬编硬解参数实战:基于Intel Media SDK的转码优化与避坑指南

1. 为什么整理这份 QSV 参数清单 说到 Intel 硬编硬解,业内开口就是 QSV。这套以 Intel 核显和独立显卡上的专用媒体单元为核心的 SDK,能把 H.264 / HEVC 的编解码、分辨率缩放、色彩转换、降噪等重活从 CPU 上搬走,转码场景里比软编省电得多…

2026/10/14 23:00:49

AI论文高效筛选实战指南:信号词矩阵与三阶验证法

1. 这不是论文搬运工,而是一份“可执行”的AI研究导航图你点开arXiv首页,面对每天新增的300篇AI方向论文,第一反应是什么?是立刻点开标题看摘要,还是下意识滑到页面底部找“Submitted on”时间戳,再默默关掉…

2026/10/14 23:00:49

Scrapy爬虫实战:以百度搜索页为例的入门项目解析

1. 为什么拿百度搜索页当Scrapy入门练手:需求边界与预期管理 1.1 这个项目到底在爬什么 Scrapy 爬虫框架,新手接触它时最大的问题不是不知道它有多强,而是不知道用它做的第一个项目该长什么样。我见过不少人捧着一本爬虫书,把目录…

2026/10/14 23:00:49

参数越多越容易过拟合?Double Descent如何颠覆你的模型容量直觉

1. 经典机器学习里那个“常识”是怎么来的——从Bias-Variance到过拟合直觉不知道你有没有一种感觉:深度学习领域里流传着太多“反直觉”的结论,而其中最反直觉、也最常被拿出来讨论的,就是大模型明明参数多到能把训练集倒背如流,…

2026/10/14 23:00:49

2026论文降重工具实测盘点:从查重原理到AI改写正确用法

“2026权威盘点”这标题起得有点大,我其实不是权威,就是个被论文折磨过、也帮不少人改过论文的过来人。但我可以保证,下面写的每一句,都来自这几年跟查重系统、降重工具实打实搏斗出来的经验。2026年了,查重系统的算法…

2026/10/14 23:00:49

seaborn热力图实战:从入门到参数调优与案例详解

第一次真正意识到热力图的价值,是在处理一份几十列的业务报表的时候。表格里密密麻麻全是数字,我盯着相关性系数来回看,眼睛都快失焦了,还是说不清哪些变量之间有关联。后来用 seaborn.heatmap 把相关性矩阵画成一张图&#xff0c…

2026/10/14 22:55:28

Scikit-learn入门实践:鸢尾花分类模型构建全流程

1. 项目概述与思路拆解1.1 为什么用Scikit-learn开启机器学习我不止一次在社区里看到有人提问:学机器学习到底应该从框架开始,还是从数学推导开始?我的答案一直很明确——先跑通一个完整的模型,再回头补理论。Scikit-learn就是最适…

2026/10/14 11:31:03

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/14 17:41:07

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/14 2:32:55

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/14 0:00:33

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:33

macOS/iOS高精度计时:mach_absolute_time原理与实战

1. 项目概述:为什么mach_absolute_time是 macOS/iOS 时间测量的“黄金标准”在 macOS 和 iOS 开发中,我见过太多人用CFAbsoluteTimeGetCurrent()或NSDate.timeIntervalSinceReferenceDate做性能打点,结果在高精度场景下被系统时钟跳变、NTP 同…

2026/10/14 0:00:33

SCORM课件演示与测试全解析:从API通信到自动化验证

简介:这是一套遵循SCORM标准的交互式课件演示与测试资源,面向E-Learning课程开发者、LMS平台测试人员及在线教学从业者,用于验证课件在不同学习管理系统中的识别、传递与跟踪能力。压缩包共28个文件,约12.07MB,以swf动…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑