直接用pandas.read_excel()循环读取会卡死,因其每次调用都重启Excel引擎、初始化开销大,数万文件导致内存持续增长、IO阻塞,常卡住或报MemoryError/OSError: [Errno 24]。

为什么直接用pandas.read_excel()循环读取会卡死
不是Excel文件大,而是每个read_excel()调用都会启动一次Excel引擎(底层依赖openpyxl或xlrd),初始化开销高;数万个文件反复加载引擎,内存持续增长、IO频繁阻塞,常见现象是进程卡在read_excel()不动,或报MemoryError或OSError: [Errno 24] Too many open files。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 改用
openpyxl.load_workbook(filename, read_only=True)手动加载工作簿,跳过样式/公式解析,速度提升3–5倍 - 用
glob.glob()一次性获取所有路径,避免嵌套os.walk()导致路径重复或遗漏 - 务必在每次读取后显式调用
wb.close()(read_only=True模式下不自动释放句柄) - 禁用
pd.concat()逐个追加——改用列表收集DataFrame,最后一次性pd.concat(dfs, ignore_index=True)
如何跳过空文件、损坏文件和非数据Sheet
小Excel常含说明页、封面页、空白行,直接读会引入NaN或列错位。不能靠skiprows硬写数字,得动态判断。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 用
openpyxl.load_workbook(..., read_only=True)先获取sheetnames,过滤掉'Sheet1'以外的默认名(如'说明'、'目录') - 对每个Sheet,用
ws.iter_rows(min_row=1, max_row=5, values_only=True)只读前5行,检查是否存在有效表头(比如含“订单号”“日期”等关键词) - 跳过
ws.max_row == 1 and all(cell is None for cell in next(ws.iter_rows(values_only=True)))的空Sheet - 捕获
InvalidFileException(损坏)、PermissionError(被占用)、KeyError(无指定sheet)并记录日志,不中断流程
合并时如何保持列顺序一致且避免类型混乱
小Excel来源杂,同一列可能在不同文件中是字符串、数字甚至混合类型,pd.concat()默认按列名对齐但不校验dtype,结果里出现object列混着int/str,后续.sum()直接报错。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 预先定义统一schema:
schema = {'订单号': str, '金额': 'float64', '日期': 'datetime64[ns]'} - 读取单个Sheet后立即用
df.astype(schema, errors='ignore')强转,失败则留原类型,后续再清洗 - 用
pd.concat(..., join='outer', sort=False),避免自动重排序列;再用df = df[schema.keys()]强制列序 - 对日期列额外加
pd.to_datetime(df['日期'], errors='coerce'),把非法值转为NaT
内存撑不住时必须流式处理
即使优化读取,10万×100行的DataFrame也轻松超2GB。别等全读完再合并,要边读边写。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 设批次大小
BATCH_SIZE = 500,每读500个文件就pd.concat()一次,立刻to_parquet()存临时分片(比CSV快且支持类型保留) - 最后用
pd.read_parquet('temp_*.parquet')配合glob一次性加载所有分片再合并 - 关键:用
gc.collect()在每批次后触发垃圾回收,防止openpyxl.Workbook对象残留 - 若连Parquet都写不动,直接用
csv.writer写纯文本中间文件——放弃索引、忽略dtype,仅保原始值
read_only=True没配close(),以及没预判空Sheet导致iter_rows()返回空迭代器却仍传给pd.DataFrame(),生成零行DF拖慢concat。


















