结论:openpyxl慢的根源在于未启用read_only=true、未限制iter_rows范围及未设values_only=true;开启后内存降至文件本体大小,加载提速数倍,且需主动中断空行、避免切片语法、及时close。

直接说结论:不是openpyxl“慢”,而是你没关read_only,也没跳过空行和无效区域——默认全量加载+全解析,100MB文件就能吃光8GB内存。
openpyxl.read_only=True 为什么必须加
openpyxl 默认会把整个 .xlsx(本质是 ZIP 包)解压、构建 DOM 树、缓存样式/公式/合并单元格信息,哪怕你只读一列。加 read_only=True 后,它跳过所有格式解析,只按 XML 流式拉取原始值,内存占用能从 5–8 倍降到接近文件本体大小。
- 不加
read_only=True:打开 300MB Excel 可能瞬间占满 4GB 内存,load_workbook()卡住十几秒甚至报MemoryError - 加了之后:同一文件通常 2–3 秒内完成加载,且后续
iter_rows()不再触发额外内存峰值 - 注意:
read_only=True下不能写入、不能访问cell.font或cell.border等样式属性,但读数据完全够用
iter_rows() 的 min_row/max_row 和 values_only=True 必须配齐
即使开了只读,ws.iter_rows() 默认仍会遍历全部已标记“非空”的行——Excel 里手动拉过滚动条的区域、删过内容但没清格式的行,都会被算进去。不设范围,可能白跑 5 万行空数据。
- 务必显式指定
min_row=2(跳过表头)、max_row=100000(预估上限),避免无限迭代 - 强制加
values_only=True:否则返回的是Cell对象,每个都带引用开销,比纯 Python 元组慢 3–5 倍 - 遇到第一行
row[0] is None就break,比 pandas 的dropna()省时省内存,因为不建中间 DataFrame
chunksize=5000 不是万能解药,得看你怎么用
pandas.read_excel(..., chunksize=5000) 看似分块,但底层仍用 openpyxl 全量加载工作表再切片——对超大文件,read_excel 调用本身就会卡死,根本到不了分块逻辑。
- 真正有效的 chunk 是在 openpyxl 层自己控:比如每 5000 行
yield一个 list,处理完立刻丢弃 - 如果要入库,别用
df.to_sql(),改用engine.execute(text("INSERT..."))+executemany(..., batch),每批 1000–2000 行最稳 - 导入前关外键检查:
SET FOREIGN_KEY_CHECKS=0(MySQL)或SET CONSTRAINTS ALL DEFERRED(PostgreSQL),完事后重建索引
为什么你试了还是卡?检查这三点
很多人照着做仍卡住,大概率栽在这三个隐形坑里:
- 文件里有隐藏的“超长空行”:Excel 手动下拉过最后一行,导致
ws.max_row返回几百万,iter_rows()死循环遍历——必须靠if row[0] is None: break主动中断 - 用了
ws['A1:C10000']这种切片语法:它会一次性生成全部 Cell 对象,内存爆炸,永远别这么干 - 忘了
wb.close():openpyxl 不自动释放资源,多次运行后残留句柄拖慢整机 I/O
真正的流式读取,是让数据“流过”你的代码,而不是“堆在”内存里等你处理。卡顿的根源从来不在文件大小,而在你有没有切断那根把整张表拽进 RAM 的绳子。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











