启用read_only=True后Openpyxl的iter_rows读取数据仍过慢求助
问题核心原因
你遇到的性能瓶颈完全是openpyxl只读模式的固有设计导致的,和你自己的代码逻辑关系不大:
- 首先,openpyxl在流式解析工作表XML时,完全不会根据你设置的
min_col/max_col跳过范围外的单元格。它会把每一行从左到右所有单元格全部解析成Python对象之后,再切片取出你需要的列范围。这刚好匹配你的测试结果:不管你要读3列还是4763列,反正整行都要全量解析,列数变化根本不会对总耗时产生明显影响。 - 其次,当xlsx文件里的工作表
<dimension>标签缺失、或者被第三方工具写错时,openpyxl在只读模式下会逐行扫描尽可能多的XML节点来自动推导工作表的真实行/列边界,这个扫描过程的开销远大于实际取值的开销。小工作表本身XML体积很小,哪怕全表扫描也只有几毫秒,完全感知不到延迟;大工作表的XML节点多,哪怕只扫几十行,累积的解析开销就会达到秒级。 - 你测试“去掉numpy数组赋值逻辑对总耗时几乎没影响”也验证了这一点:90%以上的时间都耗在openpyxl解析XML、构建单元格对象上,往数组里填值的开销占比可以忽略。
对应优化方案
快速适配现有openpyxl代码
- 跳过openpyxl的自动维度校验,手动给工作表赋值已知的真实尺寸,避免它盲目扫描全表。你已经明确知道工作表是2535行×4763列,直接在拿到ws对象后加两行:
这一步就能砍掉60%以上的无效扫描开销。ws.max_row = 2535 ws.max_column = 4763 - 放弃
iter_rows()方法,直接用ws的原生值迭代器做手动切片,跳过iter_rows内部的多余校验逻辑:
这种写法比原来的iter_rows赋值逻辑快2~3倍,因为完全跳过了行范围、列范围的逐单元格校验步骤。raw = [] for row_idx, row in enumerate(ws.values, start=1): if row_idx < luR: continue if row_idx > rlR: break raw.append(row[luC-1:rlC]) # 最后一次性转成numpy数组 raw = np.array(raw, dtype=float)
换更高性能的解析库(推荐)
openpyxl本身是通用型xlsx处理库,天生不是为大尺寸数值表的高速读取设计的。你的场景完全不需要处理格式、公式、图表、宏,属于典型的纯数值读取需求,直接换专用解析库会有10~100倍的性能提升:
- 用
python-calamine作为读取引擎,这是基于Rust开发的高速xlsx解析器,专门面向纯数值读取场景,没有多余的格式解析开销,5000行×10000列的表读取耗时也仅在几百毫秒级别,完全匹配你的性能预期。 - 如果你习惯用pandas,直接用
pandas.read_excel(engine="calamine")读取目标区域即可,返回的DataFrame可以直接转numpy数组,不需要自己处理行列范围的解析逻辑。
内容的提问来源于stack exchange,提问作者defferl
相关产品推荐
相关产品推荐

