You need to enable JavaScript to run this app. 启用read_only=True后Openpyxl的iter_rows读取数据仍过慢求助
优惠活动
大模型
产品
解决方案
定价
更多

启用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代码

  1. 跳过openpyxl的自动维度校验,手动给工作表赋值已知的真实尺寸,避免它盲目扫描全表。你已经明确知道工作表是2535行×4763列,直接在拿到ws对象后加两行:
    ws.max_row = 2535
    ws.max_column = 4763
    
    这一步就能砍掉60%以上的无效扫描开销。
  2. 放弃iter_rows()方法,直接用ws的原生值迭代器做手动切片,跳过iter_rows内部的多余校验逻辑:
    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)
    
    这种写法比原来的iter_rows赋值逻辑快2~3倍,因为完全跳过了行范围、列范围的逐单元格校验步骤。

换更高性能的解析库(推荐)

openpyxl本身是通用型xlsx处理库,天生不是为大尺寸数值表的高速读取设计的。你的场景完全不需要处理格式、公式、图表、宏,属于典型的纯数值读取需求,直接换专用解析库会有10~100倍的性能提升:

  • 用python-calamine作为读取引擎,这是基于Rust开发的高速xlsx解析器,专门面向纯数值读取场景,没有多余的格式解析开销,5000行×10000列的表读取耗时也仅在几百毫秒级别,完全匹配你的性能预期。
  • 如果你习惯用pandas,直接用pandas.read_excel(engine="calamine")读取目标区域即可,返回的DataFrame可以直接转numpy数组,不需要自己处理行列范围的解析逻辑。

内容的提问来源于stack exchange,提问作者defferl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.05 01:54:09