Python怎么高效读取大型Excel文件避免内存溢出

何川
何川
发布时间:2026-10-26 | 8人浏览过
原创

处理大型 Excel 文件(通常在几百兆字节或数十万行以上)时,使用常规的 pandas.read_excel() 极易触发系统的 Out-Of-Memory(内存溢出)崩溃。解决这一问题的核心在于打破“全量加载到 DOM 模型”的解析惯性,转向基于事件驱动的按行流式扫描(如 openpyxl 的只读模式)、高性能底层原生解析器(如基于 Rust 的 calamine 引擎)或格式降维与分块入库。本文将深入剖析内存暴涨机制,并提供生产环境中行之有效的低内存处理方案。

一、为什么大型 Excel 文件在 Python 中会引发内存暴涨

许多开发者疑惑,一个原本只有 80MB 的 .xlsx 文件,为什么使用 Python 读取时会瞬间占满 2GB 乃至 4GB 内存?原因在于 .xlsx 文件的本质是一个经过 Zip 压缩的 XML 包集合。磁盘上的几十兆字节实际上包含了被极高压缩比打包的结构数据。

当 Python 默认库打开此类文件时,必须经历两个显著放大内存的阶段:首先是解压出体积达数倍的原始 XML 文本;其次是默认解析器采用 DOM(文档对象模型)结构,为每一个单元格实例化包含样式、坐标、超链接、数据类型等大量元数据的 Python 对象。在 64 位 CPython 中,每个轻量级对象均存在固定的内部指针与字典开销,使得最终驻留内存的数据量往往膨胀到原压缩文件的 10 至 30 倍。

Python怎么高效读取大型Excel文件避免内存溢出
图 1:传统 DOM 全量解析与流式解析机制的内存开销对比

二、核心方案一:使用 openpyxl 的 read_only 模式

openpyxl 是 Python 处理 Excel 最常用的类库之一。默认情况下,其 load_workbook() 会载入整个工作簿结构。但只要开启 read_only=True,它就会切换到底层基于 SAX 的 XML 迭代流解析器,按需从压缩包读取行,处理完毕后立即交由垃圾回收,从而将内存占用锁死在极低的恒定水位。

1. 基础流式遍历用法

下面的代码演示了如何使用 read_only=True 逐行遍历大型工作簿,即使处理几百万单元格的文件,常驻内存也能保持在几十兆以内:

import openpyxl

def stream_read_excel(file_path, sheet_name=None):
    # read_only=True 启用低内存流式解析,data_only=True 直接获取单元格求值结果而非公式文本
    wb = openpyxl.load_workbook(filename=file_path, read_only=True, data_only=True)
    
    try:
        # 获取指定工作表或默认第一个工作表
        ws = wb[sheet_name] if sheet_name else wb.active
        
        # iter_rows 逐行产生元组,values_only=True 仅返回纯值以规避 Cell 对象构造
        row_iterator = ws.iter_rows(values_only=True)
        
        # 提取表头
        headers = next(row_iterator, None)
        print(f"数据表头: {headers}")
        
        count = 0
        for row in row_iterator:
            count += 1
            # 在此处处理业务逻辑(如校验、清洗或写入数据库)
            if count % 10000 == 0:
                print(f"已流式处理 {count} 行数据...")
                
        print(f"处理完成,总计读取有效数据行数: {count}")
    finally:
        # 必须显式关闭工作簿以释放文件句柄及解压流
        wb.close()

注意事项:在 read_only=True 模式下,工作表游标只能单向向前迭代,无法回溯,也不支持写入或随机单元格索引(例如 ws['C15'] 会触发扫描异常)。使用完毕后必须显式调用 wb.close(),避免文件句柄泄露。

2. 分块封装与转换为 Pandas DataFrame

如果后续数据分析必须借助 Pandas,可以将流式行发生器打包成 Batch Generator,批量生成指定大小(如每批 10,000 行)的小型 DataFrame,实现“分块吞吐”:

import openpyxl
import pandas as pd

def excel_chunk_generator(file_path, chunk_size=10000):
    wb = openpyxl.load_workbook(file_path, read_only=True, data_only=True)
    try:
        ws = wb.active
        row_iter = ws.iter_rows(values_only=True)
        headers = next(row_iter, None)
        if headers is None:
            return
            
        chunk = []
        for row in row_iter:
            chunk.append(row)
            if len(chunk) >= chunk_size:
                yield pd.DataFrame(chunk, columns=headers)
                chunk = []
                
        if chunk:
            yield pd.DataFrame(chunk, columns=headers)
    finally:
        wb.close()

# 消费分块数据
for i, df_chunk in enumerate(excel_chunk_generator("huge_dataset.xlsx", chunk_size=20000)):
    # 模拟分块入库或聚合统计
    print(f"正在处理第 {i+1} 块,包含 {len(df_chunk)} 行数据")

三、核心方案二:利用 python-calamine 引擎实现极速读取

虽然 openpyxl 的只读模式解决了内存耗尽问题,但由于其内部核心仍是纯 Python 构建的解析逻辑,在面对几十万行数据时处理耗时较长。calamine 是一个由 Rust 编写的高性能 Excel 读取引擎。通过其 Python 绑定 python-calamine,能够在获得超低内存占用的同时,带来相比原生 Python 库 5 到 15 倍的解析速度提升。

1. 在 Pandas 2.2+ 中直接使用 calamine 引擎

现代版本的 Pandas(2.2 及以上)已原生将 calamine 纳入可选引擎。只需预先安装 pip install python-calamine,即可直接无缝加速:

import pandas as pd

# 安装依赖:pip install python-calamine
# 切换引擎为 calamine,无需改动 DataFrame 处理逻辑
df = pd.read_excel(
    "large_dataset.xlsx",
    engine="calamine"
)
print(f"数据加载完成,形状: {df.shape}")

2. 原生使用 python-calamine 流式提取

如果希望完全剥离 Pandas 的开销,直接使用底层 CalamineWorkbook 可以做到极速吞吐与微量内存常驻:

from python_calamine import CalamineWorkbook

def read_by_calamine_native(file_path):
    # 底层由 Rust 驱动,秒级打开超大工作簿
    wb = CalamineWorkbook.from_path(file_path)
    sheet_name = wb.sheet_names[0]
    
    # 获取工作表并迭代每一行
    rows = wb.get_sheet_by_name(sheet_name).to_python()
    headers = rows[0]
    
    for row in rows[1:]:
        # row 是原生 Python 列表,无复杂对象封装
        pass
    print("底层 Rust 引擎读取完毕")
Python怎么高效读取大型Excel文件避免内存溢出
图 2:针对大型 Excel 文件的技术选型决策路径

四、架构级优化:格式转换与专用查询引擎

在数据工程实践中,如果一个文件体积持续增长且需要高频分析,将 .xlsx 格式作为数据载体本身就是性能瓶颈所在。此时最稳妥的做法是通过脚本单次流式将其转换为适合大数据处理的格式。

1. 转为 CSV 后使用 chunksize 分块读取

许多开发者误以为 pd.read_excel() 也有类似 pd.read_csv(chunksize=...) 的原生低内存分块机制,但实际上由于底层 Excel 结构限制,某些引擎即使设置了分块依然会在内存中完整解包。因此,将数据先流式转为 CSV 才是真正释放分块威力的方案:

import csv
import openpyxl
import pandas as pd

def convert_excel_to_csv_stream(excel_path, csv_path):
    wb = openpyxl.load_workbook(excel_path, read_only=True, data_only=True)
    ws = wb.active
    
    with open(csv_path, mode="w", newline="", encoding="utf-8") as f:
        writer = csv.writer(f)
        for row in ws.iter_rows(values_only=True):
            writer.writerow(row)
    wb.close()
    print("CSV 转换完成,无内存峰值")

# 转换后即可享受 Pandas 原生低内存极速 chunksize 分块处理
# for chunk in pd.read_csv("dataset.csv", chunksize=50000):
#     process(chunk)

2. 使用 DuckDB 直接查询 Excel

嵌入式分析型数据库 DuckDB 拥有出色的内存管理机制与空间处理能力。借助其官方空间与 Excel 扩展插件,可以直接使用 SQL 语法对磁盘上的 .xlsx 文件执行过滤与聚合,而无需在 Python 进程中实例化任何中间数据:

import duckdb

# 创建内存数据库连接并加载 spatial/excel 扩展
con = duckdb.connect()
con.execute("INSTALL spatial; LOAD spatial;")

# 直接执行 SQL 进行投影与聚合,内存由 DuckDB 严格限制
query = """
SELECT department, SUM(salary) as total_salary, COUNT(*) as count
FROM st_read('massive_staff.xlsx')
WHERE status = 'Active'
GROUP BY department
"""
result_df = con.execute(query).df()
print(result_df)

五、生产环境标准实施流程

为了确保在内存严格受限的生产容器(如限额 512MB 的 Docker 实例)中稳定读取大文件,建议遵循以下五步处理闭环:

Python怎么高效读取大型Excel文件避免内存溢出
图 3:大型 Excel 文件在低内存环境下的标准操作生命周期

六、方案综合对比与总结

为了直观评估各类方案的技术特性,可参考以下横向对比指标:

方案名称 内存占用水平 解析速度 外部依赖要求 典型推荐场景
Pandas 默认 (openpyxl) 极高 (文件原体积 10-30 倍) 慢 仅 pandas, openpyxl 小于 20MB 的小文件快速探索分析
openpyxl 只读模式 极低 (恒定 30MB ~ 60MB) 中等 仅 openpyxl 超大文件的批处理入库、数据清洗
python-calamine 低 (轻量级内存) 极快 (Rust 驱动) 需安装预编译 C/Rust 轮子 几十万行数据的整表分析与交互运算
DuckDB 查询引擎 极低 (原生流式磁盘缓存) 极快 duckdb, spatial 插件 海量复杂聚合、跨表过滤与离线报表
Python怎么高效读取大型Excel文件避免内存溢出
图 4:综合维度评分与方案落地最终建议

在 Python 中规避大型 Excel 文件内存溢出并非难事。只要理清底层 XML 解压对象化的机制,摒弃整表一次性加载的旧思路,结合流式游标迭代或高效率 Rust 引擎,就能以极低且恒定的内存轻松化解大文件处理难题。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表