我经常会收到两类消息:一种是"用Python写Excel是不是很难",另一种是"为什么我装了pythonexcel这个库却用不了"。先说结论——在Python的世界里,并不存在一个叫"pythonexcel"的标准库,大家普遍这么称呼的,其实是一整套围绕Python与Excel文件打交道的工具集合。只要把其中的核心库用熟练,读报表、写数据、批量处理、自动生成周报这些需求,脚本一跑就能出结果。这篇文章我会从为什么值得学这套东西讲起,到环境搭建、核心读写、批量自动化,再到一堆我实际踩过的坑,全部整理出来,给正在搜"python excel库"的朋友一份可以直接照着做的路线图。
1. 从"手搓Excel"到"Python自动化":我为什么最终切到脚本处理
1.1 我最初的Excel困境
事情要从好几年前说起。当时我在一家做零售数据分析的小组里,每周五下午都要把十几个门店发来的Excel报表合并成一张总表。每个门店的表格格式还不完全一样,有的把销售额放在B列,有的放在D列,有的日期格式是2024-01-05,有的却写成了2024年1月5日。我每周都要手动打开每一个文件,复制粘贴,调整格式,检查合计。一次两次还可以接受,连续做了几个月之后,真的受不了了——这种纯重复的体力劳动,既没有技术含量,又容易出错。有一次我不小心把某门店的销售额漏掉了,直接被领导叫去谈话,那之后我就下定决心要用脚本把这套流程自动化。
当时我搜索的关键词,就是"python excel"。
1.2 Python方案真正解决的三类问题
使用Python处理Excel,解决的绝不仅仅是"不用手动点开文件"这么简单。我把它归纳成三大类核心场景,这也是你判断自己是否需要这套技术的关键。
第一类是数据整合场景。也就是我上面说的那种问题:几十个Excel文件,格式大同小异,需要汇总成一张总表。纯手工操作的问题不只是慢,更在于"人的注意力会退化"。到第30个文件的时候,你已经很难保持和第3个文件一样的仔细程度。而Python脚本只要写一次,跑一万次都不会烦躁走神。
第二类是批量操作场景。比如你需要给一百个客户分别生成一份定制的对账单,每份账单的标题、金额、联系人都不一样,其他格式完全统一。手工做这件事,意味着要复制一百遍模板,再改一百遍内容,费时耗力。如果用脚本,设计好模板之后,一秒钟生成一个文件,一百个文件几分钟搞定。
第三类是数据分析场景。Excel自带的筛选、透视表、公式功能很强大,但遇到一些稍微复杂的数据清洗、跨表关联、条件统计时,操作步骤会非常繁琐,而且很难复用。如果用Python的pandas库处理,Excel表格会被当作一个结构化的数据集,可以用一行代码完成分组求和、数据去重、缺失值填充。写过的处理逻辑还可以封装成函数,下次遇到同样的数据结构,换个文件路径就能直接跑。
1.3 什么人适合学这套东西
我把话说明白点,不是所有人都需要学这套东西。如果你是Excel轻度用户,一个月也用不了几次,只做一些简单的数据登记和表格美化,那你没必要引入脚本,老老实实手工操作反而更快。但如果你是以下几类人,我建议你认真学一下:
- 经常需要处理别人发来的Excel文件,且文件格式不统一;
- 每天/每周有固定报表任务,比如日报、周报、月报;
- 需要把Excel数据导入数据库,或者把数据库导出成Excel;
- 从事数据分析、运营、财务、人事这类大量和表格打交道的岗位;
- 单纯想用程序的方式偷懒,省出更多时间摸鱼或者学新东西。
这套技术栈的学习成本并不高。你不需要成为Python专家,只需要掌握几个核心库的常用API,外加一点基础的Python语法,就足以解决工作中80%的Excel自动化需求。我接下来讲的所有内容,都基于这样一个定位:面向实际任务的、够用就行的方案。
2. 生态选型对照:openpyxl、pandas、xlwings到底分别管哪摊事
很多人第一次搜"python excel库"的时候会看到一堆名字:openpyxl、xlrd、xlwt、pandas、xlsxwriter、xlwings、pyexcel……当场就懵了,到底该学哪个?我当时也是同样的困惑。这里我根据自己的使用经验,把最常用的几个库梳理成一个对比表,你先有个整体认知,后面实操部分我会逐个展开。
| 库名 | 主要用途 | 支持的Excel格式 | 学习难度 | 我的使用场景 |
|---|---|---|---|---|
| openpyxl | 读写.xlsx文件,可操作单元格、样式、图表,最常用的库 | .xlsx | 较低 | 日常读写、模板填充、样式控制 |
| pandas | 数据分析,表格整体处理、聚合、清洗 | .xlsx/.xls(依赖其他库) | 中等 | 汇总分析、跨表关联、数据透视 |
| xlsxwriter | 创建带有丰富格式的.xlsx文件 | .xlsx | 较低 | 生成漂亮报表、图表导出 |
| xlrd | 读取旧版.xls文件 | .xls | 低 | 读取老系统导出的数据 |
| xlwt | 写入旧版.xls文件 | .xls | 低 | 老格式支持,现在很少用 |
| xlwings | 通过脚本操控Excel程序本身 | .xlsx/.xls | 中等 | 需调用Excel公式、宏、实时交互 |
| pyexcel | 统一封装各种Excel操作 | 多种 | 低 | 简单的跨格式转换 |
2.1 openpyxl:我日常使用频率最高的主力
如果你只想学一个库来覆盖Excel的大部分需求,我的建议是openpyxl。它直接操作.xlsx文件,不需要本机装有Excel软件就能读写。这意味着你可以在服务器上跑脚本,定时生成报表,再通过邮件发出去。这套方案我一直延用至今。
openpyxl的设计思路是模仿Excel本身的三层结构:工作簿(Workbook)、工作表(Worksheet)、单元格(Cell)。你加载一个Excel文件之后,操作过程和你平时在Excel里点来点去非常像,只是从鼠标变成了代码。它可以设置单元格的值、字体、颜色、边框、对齐方式,可以合并单元格,可以插入图片,可以添加图表,还可以设置列宽行高。日常办公能用到的Excel功能,它基本都覆盖了。
但openpyxl有两个明显的限制:第一,它不支持旧版的.xls格式,只支持.xlsx;第二,它对Excel里的公式并不是"计算"态度,而是"记录"态度。也就是说,如果你用openpyxl写了一个公式进去,Excel打开这个文件时会自动计算,但如果你用openpyxl读取一个带公式的单元格,默认拿到的可能就是公式字符串本身,而不是计算出的结果。这个问题我在后面的坑位部分会重点讲。
2.2 pandas:处理整张表数据的效率之王
如果说openpyxl是把Excel当一个文件来操作,那么pandas就是把Excel当一个数据集来操作。它底层把表格读进来后,会转换成一个DataFrame对象。你可以把它想象成一张增强版的Excel表:有行索引、有列名,还内置了大量数据处理方法。
举个例子,你想求每个区域的销售额总和。在Excel里你需要插入透视表,或者用SUMIF函数。在pandas里,两行代码就完成了——先按区域分组,再求和。如果你做的"Excel处理"本质上更多是数据分析,而不是格式编辑,那pandas才是你的主力,openpyxl反而只是辅助工具。
pandas本身不能直接读写Excel文件,它需要依赖某个底层引擎。最新版本推荐使用openpyxl作为引擎,所以这两者多数情况下是搭配使用的。pandas负责算数,openpyxl负责样式,各司其职。
2.3 xlwings:需要和Excel软件联动时的"遥控器"
xlwings的性质和openpyxl完全不同。openpyxl是绕过Excel程序直接操作文件,xlwings则是启动本机的Excel软件,然后通过脚本远程操控它。你可以让脚本打开一个Excel工作簿,往里填写内容,调用Excel自身的函数,甚至让Excel执行VBA宏。
这个库适合什么场景呢?比如你有一个别人写好的、带复杂宏的Excel流程,不想重写成Python版本,只想用Python当调度器,自动触发它跑一遍,那么xlwings是很好的选择。又比如你写了个数据处理脚本,算完之后想直接在一个已经打开的Excel文件里刷新透视表,也可以用到它。但要注意,xlwings必须在装有Excel的电脑上运行,Linux服务器上没法用。所以它的应用范围其实比openpyxl小得多,主要是桌面端自动化。
2.4 选型建议
我给新手的建议很简单:先学openpyxl,因为它最通用,能覆盖读写和样式两个基本诉求;然后学pandas,因为数据分析是Excel自动化最硬的需求;最后根据工作环境决定是否学xlwings。其他几个库,用到再说,现在不用管。
3. 三分钟跑通环境:安装Python库和第一段读写脚本
3.1 别绕过Python环境的准备
我知道现在网上Python安装教程一搜一大把,有些新手折腾了一天环境还没配好。这里我只说最关键的几点。首先去Python官网下载Windows安装包,安装时务必勾选"Add Python to PATH",这个选项默认是不勾的,不勾的话后面在命令行里输入python会提示找不到命令,非常麻烦。Mac用户建议装Homebrew之后用brew install python3。Linux用户一般自带Python,但版本可能偏老,建议用pyenv管理版本。
装好Python之后,打开命令行,输入python --version,如果能看到版本号,说明环境OK。下一步建议创建一个虚拟环境,避免不同项目的依赖包互相干扰。我用的是venv:
python -m venv excel_env excel_env\Scripts\activate # Windows激活虚拟环境 source excel_env/bin/activate # Mac/Linux激活虚拟环境看到命令行前面出现了(excel_env)字样,说明虚拟环境已经激活,后面安装的库都装在这个独立环境里,不会污染系统。
3.2 安装Excel处理的核心库
接下来安装openpyxl和pandas。打开命令行工具,输入:
pip install openpyxl pip install pandas如果你的网络比较慢,可以换用国内镜像源:
pip install openpyxl pandas -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后,可以用一条简单的命令验证是否装好:
python -c "import openpyxl, pandas; print('OK')"如果控制台输出了OK,说明两个库都能正常导入了。这是最基础的排障方法,别小看这一步,很多人后面报错都是因为库没装上或者装错了环境。
3.3 第一个脚本:读取工作簿结构
安装完库,我们写第一段真正能跑通的代码。假设当前文件夹里有一个"销售数据.xlsx"文件,里面有几个工作表。我们来打印它的结构:
import openpyxl # 加载工作簿 wb = openpyxl.load_workbook('销售数据.xlsx') print('工作表列表:', wb.sheetnames) # 获取第一个工作表 ws = wb.active print('当前工作表名称:', ws.title) print('表格尺寸:', ws.dimensions) # 打印前5行数据 for row in ws.iter_rows(min_row=1, max_row=5, values_only=True): print(row)这段代码做了三件事:加载文件、探查有哪些sheet、读取前几行数据。iter_rows方法按行遍历,values_only=True表示只返回值、不返回单元格对象,适合快速预览数据。跑完这段,你就能看到一个Excel文件的基本面貌了。
3.4 理解Excel的三层结构
初学者最容易困惑的是load_workbook和Workbook的区别。load_workbook是读取一个已存在的文件,Workbook()是新建一个空白文件。你可能还会遇到这样的情况:明明改了一个单元格的值,保存后再用Excel打开,发现其他单元格的样式丢失了。这是因为如果原文件里有一些特殊内容(如图表、图片、部分高级格式),openpyxl在保存时可能无法完整保留,所以在修改别人发来的复杂表格时,最好先备份原文件。
我一直觉得,理解Excel文件在Python里的三层结构是入门的关键。工作簿是最大的容器,相当于文件的本身;工作表是工作簿里的每一页,通常用底部的标签来区分;单元格是最小单位,通过类似'B2'这样的坐标来定位。掌握了这三层,后面所有操作都只是在这三层之间存取数据而已。
4. 核心读写实操:从单个单元格到整表数据流转
4.1 新建工作簿并写入基础数据
环境搞定之后,我们开始动手写数据。先从一个最简单的例子开始:创建一个新的Excel文件,写入一个客户列表。
import openpyxl from openpyxl.styles import Font, Alignment, PatternFill, Border, Side wb = openpyxl.Workbook() ws = wb.active ws.title = '客户列表' # 写入表头 headers = ['姓名', '城市', '消费金额'] ws.append(headers) # 写入数据行 ws.append(['张三', '北京', 1200]) ws.append(['李四', '上海', 890]) ws.append(['王五', '广州', 1560]) # 设置表头样式:加粗、居中、背景色 for cell in ws[1]: cell.font = Font(bold=True) cell.alignment = Alignment(horizontal='center') cell.fill = PatternFill(start_color='DDEBF7', end_color='DDEBF7', fill_type='solid') # 设置列宽 ws.column_dimensions['A'].width = 12 ws.column_dimensions['B'].width = 12 ws.column_dimensions['C'].width = 12 wb.save('客户列表.xlsx')这里有几个值得注意的细节。append方法会在当前工作表末尾追加一行数据,传入一个列表,依次填入各列。写入的数值类型就是Python的原生类型:字符串、整数、浮点数都会原样保存,不会自动转成文本。样式设置是通过Font、Alignment、PatternFill这些类来完成的,它们都属于openpyxl.styles模块。每个样式类都是由若干属性组成的,比如Font可以设置名字、大小、加粗、颜色;Alignment可以设置水平垂直对齐和换行。
4.2 读取已有数据并做简单计算
读取和写入是基本对称的。我们读回刚才创建的文件,计算所有客户的消费总额和平均值。
import openpyxl wb = openpyxl.load_workbook('客户列表.xlsx') ws = wb.active total = 0 count = 0 for row in ws.iter_rows(min_row=2, values_only=True): name, city, amount = row total += amount count += 1 print(f'客户数量:{count},消费总额:{total},人均消费:{total / count:.2f}')这段代码演示了如何遍历数据区域。min_row=2表示跳过表头,从第二行开始。values_only=True让我们直接拿到数值而不是单元格对象。实际开发中你可能有几十列数据,此时把每行的数据都解包出来是不现实的,更好的做法是构建一个列表,把所有行数据作为一个二维列表拿到,再交给pandas做统一处理。
4.3 用pandas做批量统计分析
下面我们来看pandas的典型用法。假设我们有一个包含多个月份销售明细的Excel文件,每个工作表代表一个月的销售记录,结构相同,都要计算每个商品的销售额合计。
import pandas as pd # 读取第一个工作表 df = pd.read_excel('销售明细.xlsx', sheet_name=0) print('数据预览:') print(df.head()) print('数据维度:', df.shape) # 假设表里有 商品名称 单价 数量 三列 df['销售额'] = df['单价'] * df['数量'] result = df.groupby('商品名称')['销售额'].sum().reset_index() result = result.sort_values('销售额', ascending=False) # 导出结果 result.to_excel('商品销售汇总.xlsx', index=False)pandas的read_excel函数返回DataFrame,head()可以快速预览前几行,shape属性返回行数和列数。groupby是数据分析里最高频的方法之一,它把一个DataFrame按某一列分组,然后对另一个列应用聚合操作。这里按商品名称分组,对销售额列求和,得到一个汇总结果。reset_index的作用是让分组键变成普通列,不至于留在索引里,这样输出到Excel格式更直观。
我为什么建议大家在处理数据统计时优先用pandas?因为openpyxl算数据需要你手动写循环去逐个单元格取值,数据量一大就慢,而且代码啰嗦。而pandas的方法是声明式的——你想"按商品分组求和",代码里就写groupby('商品').sum(),语义和意图完全一致,读代码的人不需要猜你在干什么。尤其对方格超过几万行的Excel,pandas的数据处理速度和处理体验都远好于手工遍历单元格。
4.4 单元格样式:让脚本生成的报表不再丑
数据有了,格式也不能太难看。用openpyxl生成的报表,经常给人一种"程序生成"的冷冰冰感觉,那是因为默认情况下,所有单元格都是宋体、无边框、无对齐控制。我们可以在保存之前批量设置格式。下面的代码演示了如何给已有数据添加边框和颜色:
from openpyxl.styles import Border, Side thin_border = Border( left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin') ) ws = wb.active for row in ws.iter_rows(min_row=1, max_row=ws.max_row, max_col=ws.max_column): for cell in row: cell.border = thin_border if cell.row % 2 == 0: cell.fill = PatternFill(start_color='F2F2F2', end_color='F2F2F2', fill_type='solid')这里做了一件实用的事:给整张表加上细边框,并且给偶数行加上浅灰色填充,实现隔行变色。这种效果在Excel里叫斑马纹,视觉上非常提升报表质感。PatternFill的start_color和end_color设置为相同颜色,fill_type设置为solid,意思就是纯色填充。眼尖的朋友可能会问,为什么有开始色和结束色两个参数?这是为了兼容渐变填充。我们使用纯色填充时,两个值写成一样就行。
5. 自动化进阶:模板批量填充、跨表汇总与报表生成
5.1 用现成模板批量生成客户对账单
接下来这部分是我认为"Python处理Excel"最值钱的应用场景——批量生产文档。前面说到的客户对账单就是个很好的例子。假设财务部有一个设计好的"对账单.xlsx"模板,B3单元格是客户姓名,B4是订单数,B5是总金额。我们不需要从零开始造文件,只需要加载模板、填入数据、另存为新文件。
import openpyxl from pathlib import Path # 客户数据:姓名, 订单数, 总金额 customers = [ ('张三', 12, 12000), ('李四', 8, 8900), ('王五', 23, 15600), ] output_dir = Path('对账单输出') output_dir.mkdir(exist_ok=True) for name, count, amount in customers: wb = openpyxl.load_workbook('对账单模板.xlsx') ws = wb.active ws['B3'] = name ws['B4'] = count ws['B5'] = amount wb.save(output_dir / f'对账单_{name}.xlsx') print(f'已生成:{name}的对账单')这段代码的口诀就是"模板+数据=产出"。生成一百个文件,本质上就是把上述循环多跑一百遍。这里有一个非常重要的工程习惯:在循环内部每次重新load模板。因为如果只加载一次模板,第一次循环时你修改了B3单元格,第二次循环时模板已经不是原始状态了,生成的文件会互相污染。每次load_workbook('对账单模板.xlsx')就是重新读取磁盘上的原始文件,保证每轮迭代都从干净状态开始。这个细节我见过不少新人踩坑,特地说一下。
5.2 遍历文件夹中的所有Excel并汇总
我们再来看一个更复杂的场景:目录下几十个Excel文件,结构类似但顺序不完全一致,需要汇总成一张总表。这时pandas才是合适的工具。
from pathlib import Path import pandas as pd data_dir = Path('data') frame_list = [] for file in data_dir.glob('*.xlsx'): df = pd.read_excel(file, sheet_name=0) # 加一列来源文件名,方便追溯 df['来源文件'] = file.name frame_list.append(df) print(f'已读取:{file.name},行数{len(df)}') all_data = pd.concat(frame_list, ignore_index=True) all_data.to_excel('汇总表.xlsx', index=False) print(f'汇总完成,共{len(all_data)}行')glob('*.xlsx')会匹配目录下所有Excel文件,返回一个生成器。pd.concat把多个DataFrame纵向拼接,ignore_index=True会重新生成连续的行索引,避免拼接后行号混乱。如果你遇到"有的表列名相同但顺序不同",pandas会自动按列名对齐,这是一个隐藏福利;如果你遇到"有的文件里多了一列无用的备注",上面的代码也会把那一列带进来,此时你需要在循环里先筛选列,再append。我给一个筛选的示例:
df = pd.read_excel(file, sheet_name=0) df = df[['日期', '区域', '销售额']] # 只保留需要的三列5.3 把Excel当中间媒介:数据导入数据库和反向导出
Excel不只是一个最终产物,它经常扮演"中间数据容器"的角色。比如你从业务系统导出了一个Excel明细表,需要导入MySQL数据库;又比如你从数据库查了一批数据,需要导出成Excel给业务同事。这两件事用Python都可以做成一条流水线。
先看从Excel导入SQLite的简单示例:
import pandas as pd import sqlite3 df = pd.read_excel('订单明细.xlsx') conn = sqlite3.connect('business.db') df.to_sql('orders', conn, if_exists='replace', index=False) conn.close() print('导入完成')pandas的to_sql方法可以直接把DataFrame写入数据库表,if_exists='replace'表示如果表已存在就替换。反过来,数据库查询结果导出Excel也一样简单:
import pandas as pd import sqlite3 conn = sqlite3.connect('business.db') df = pd.read_sql_query('SELECT * FROM orders WHERE 销售额 > 1000', conn) conn.close() df.to_excel('高价值订单.xlsx', index=False)这套组合非常实用。我以前在电商公司做运营支持时,每天都要导出各种维度的订单数据给同事,用这种两段式流水线,只要换一下SQL查询语句,十分钟就能生成一个定制报表。
5.4 定时自动化:把报表生成交给调度器
流程写好了之后,接上定时调度,才算真正"躺平"。Windows系统可以用任务计划程序,Mac/Linux可以用crontab。以Windows为例,你把脚本保存成generate_report.py,然后在任务计划程序里新建一个任务,设置每天上午九点运行python generate_report.py。这样每天一到点,报表就自动生成、自动保存到指定路径,你甚至可以直接在脚本里调用邮件接口把文件发出去。
有一点要提醒:在定时任务里运行脚本时,Python解释器的路径请写成绝对路径。因为计划任务的环境变量和你自己敲命令的终端环境可能不一样。比如:
C:\excel_env\Scripts\python.exe D:\projects\generate_report.py如果你直接写python generate_report.py,定时任务可能因为找不到Python而报错,但你自己手动跑又正常——这个问题排查起来很迷,提前写成绝对路径能省很多事。
6. 我踩过的那些坑:类型转换、公式缓存与大文件性能
6.1 只支持xlsx不支持xls
千万不要以为"Excel文件"都是一个样子。旧版的.xls和新版的.xlsx在文件格式上完全是两回事,前者是二进制格式,后者是基于XML的压缩格式。openpyxl只处理.xlsx,不能加载.xls文件。如果你用openpyxl去加载一个.xls文件,大概率会收到一个不友好的报错,提示文件格式无效或已损坏。
解决办法分两种:其一,用Excel把.xls另存为.xlsx再处理;其二,用xlrd库读.xls文件,然后用openpyxl写回新的.xlsx。xlrd在版本2.x之后只支持读取.xls,这也意味着它和openpyxl正好互补——一个管旧,一个管新。我在处理老系统导出的表格时经常组合使用:
import xlrd import openpyxl book = xlrd.open_workbook('old_data.xls') sheet = book.sheet_by_index(0) wb = openpyxl.Workbook() ws = wb.active for r in range(sheet.nrows): ws.append(sheet.row_values(r)) wb.save('old_data_converted.xlsx')6.2 公式单元格读出来是空,或者说读的是公式字符串
这个是很多人碰到的最困惑的问题之一。你用openpyxl读取一个Excel文件,明明在Excel里能看到某个单元格显示的是计算结果,比如SUM(E2:E10)的结果是100,但openpyxl读出来却是公式字符串'=SUM(E2:E10)',或者干脆读出来是None。
原因在于.openpyxl有data_only这个参数。默认情况下,load_workbook读取的是带有公式的版本,单元格对象保存的是公式本身。如果你设置data_only=True,它尝试读取的是上次Excel计算后缓存的数值。关键在于:如果你的.xlsx文件是openpyxl生成的,写入的公式没有经过Excel实际打开计算过,那么文件里就没有缓存数值,data_only=True读出来的就是None。
一个稳妥的解法是,在写公式的同时写入缓存值,或者保证文件经过Excel打开过一次。如果你只是在脚本与脚本之间流转数据,尽量避免依赖公式计算。把公式算好的结果直接写入新的列,比让Excel去计算公式更可控。我在做自动化报表时,都是直接用pandas在Python里算好最终数值,写入Excel时写纯数字,而不是写公式。虽然这样失去了一点"动态刷新的能力",但换来的是无论谁来打开这个文件,看到的都是确定的数字,不会出现打开后数值还没计算出来的问题。
6.3 合并单元格的读取陷阱
很多业务报表喜欢把标题行合并单元格,比如"一月销售汇总"横跨A1到E1。如果你用openpyxl读取合并单元格区域,会发现在A1能读到标题,但B1、C1、D1、E1这些被合并的单元格读出来是None或者空字符串。这不是bug,这是合并单元格在底层数据结构上的特性——合并区域里只有左上角那个单元格保存实际值,其他单元格是空的占位。
处理方式有两种。如果你需要读取这类单元格的值,先判断单元格是否在合并区域中,如果是,就取合并区域左上角的值。openpyxl提供了merged_cells.ranges属性来查看所有合并区域。如果你需要创建带合并单元格的报表,可以使用ws.merge_cells('A1:E1')方法,然后在A1写入标题文字。读取时要做一些额外判断,写入时也要记得"只给左上角赋值"。这个特性在批量解析别人表格时经常遇到,值得留意。
6.4 超过65536行的大文件,性能肉眼可见地变差
老读者可能知道,旧版Excel单个工作表最多65536行,新版可以达到1048576行。如果你的数据量达到数十万行,用openpyxl逐行逐单元格操作会明显变慢,因为每操作一个单元格都有内存开销和对象创建成本。
针对这类大文件,我的经验有两个优化方向。第一,在写入时使用write_only模式:
wb = openpyxl.Workbook(write_only=True) ws = wb.create_sheet('data') ws.append(['列1', '列2', '列3']) for row_data in big_data_list: ws.append(row_data) wb.save('big_file.xlsx')write_only模式大幅减少了内存占用,适合大规模写入,但代价是你不能用它来读取或修改已有文件。第二,在读取时使用pandas的chunksize参数分块处理,避免一次性把整张表载入内存:
import pandas as pd chunk_list = [] chunk_iter = pd.read_excel('big_file.xlsx', sheet_name=0, chunksize=10000) for chunk in chunk_iter: # 对每一块做处理 result = chunk.groupby('区域')['销售额'].sum() chunk_list.append(result) final_result = pd.concat(chunk_list).groupby(level=0).sum()这种分批处理的方式在处理百万行数据时尤为重要,我处理过的最大的一份Excel有八十万行,当年没做分块导致程序直接内存溢出,改用chunksize之后一路顺畅。
6.5 不要忘了自动过滤器和冻结窗格
最后分享一个小细节。用脚本生成的Excel报表,打开后可能需要肉眼筛选某些列。如果你在脚本里就直接给表头加上自动筛选,并在标题行下设置冻结窗格,那使用者打开文件就能直接筛选、滚动查看时表头不消失,体验会好很多。
ws.auto_filter.ref = ws.dimensions # 对整个表启用筛选 ws.freeze_panes = 'A2' # 冻结第一行auto_filter.ref表示筛选作用的区域,freeze_panes指定要冻结的分界线——设置在A2单元格,意味着A2之前的行列(即第一行)被冻结。这两行代码加进去,生成的报表观感会非常接近专业运营做出来的效果。
从最早手动复制粘贴,到现在一个脚本生成几十个报表,我确实在这套组合上省下了大量时间。开头提到的"pythonexcel"并不是一个真实的库名,但围绕Python和Excel的这套生态,称它"神奇"并不夸张——openpyxl处理格式、pandas分析数据、xlwings操纵Excel本尊,三者搭配起来能覆盖绝大多数办公自动化需求。如果你正打算入门,建议从第四章的读写代码开始复制练习,先跑通一个最小案例,再逐步扩展到自己的业务场景。踩坑之后也不要灰心,这一类问题的解法往往非常简单,难点只在于第一次遇到时别慌神。