正确做法是批量读取为列表后一次性pd.concat(),用glob或pathlib获取路径,指定dtype、usecols和引擎(xls用xlrd,xlsx用openpyxl或calamine),避免循环拼接与自动类型推断。

用 pd.concat() 合并多个 DataFrame,别用循环反复 read_excel() 再 append()
反复调用 pd.read_excel() 并在循环里用 df.append()(或 pd.concat() 每次只拼两个)会导致内存暴涨、速度极慢——因为每次拼接都生成新对象,旧对象等待 GC,I/O 和对象复制开销叠加。正确做法是先批量读取为 list,再一次性 pd.concat()。
- 用
glob.glob("*.xlsx")或pathlib.Path().glob()批量获取文件路径,避免手动列名 - 用列表推导式 +
pd.read_excel()一次性读取全部:[pd.read_excel(f, engine="openpyxl") for f in files] - 传入
ignore_index=True防止合并后索引重复;若需保留原始文件来源,可加keys参数或提前插入列:df.assign(source=f.name) - 如果表格有固定表头但首行是空行或标题行偏移,统一加
skiprows=1或header=1,别让每个文件自己猜
读取时指定 dtype 和 usecols,跳过无用列和类型自动推断
默认情况下 read_excel() 会扫描全列推测 dtype,对千个大表来说非常耗时,且容易把数字列误判为 object(尤其含空值或混合内容)。显式控制能提速 30%–70%,还能避免后续 astype() 报错。
- 用
usecols="A:C,E:G"或usecols=[0,1,2,4,5,6]只读业务需要的列,跳过备注、空列、审计字段 - 对已知结构的表,直接传
dtype={"订单号": "string", "金额": "float32", "日期": "string"};"string"类型比object更省内存且支持向量化操作 - 如果“日期”列实际是 Excel 序列号(如
44562),别依赖parse_dates,改用converters={"日期": pd.TimedeltaIndex}或后期统一转:pd.to_datetime(df["日期"], unit="d", origin="1899-12-30")
处理 openpyxl 引擎报错 ValueError: Unknown extension: .xls
这是常见陷阱:你装了 openpyxl,但它只支持 .xlsx/.xlsm/.xltx/.xltm;遇到老版本 .xls 文件会直接崩。Pandas 默认不自动切换引擎,必须显式指定。
- 先检查文件扩展名:
path.suffix.lower() in [".xls", ".xlt"],这类文件必须用engine="xlrd"(注意:xlrd ≥ 2.0 不再支持.xlsx,所以不能全局设) - 安全做法是分组读取:
[pd.read_excel(f, engine="xlrd" if f.suffix.lower() == ".xls" else "openpyxl") for f in files] - 更省事的替代方案是统一用
engine="calamine"(需pip install polars[calamine],然后pd.read_excel(f, engine="calamine")),它支持所有 Excel 格式且速度更快,但要求 Pandas ≥ 2.2
内存撑爆前先做 chunksize 分块读 + 流式合并
单个 Excel 表就几十万行,上千个加起来超 10GB?别硬刚。Pandas 的 read_excel() 不支持 chunksize,但可以用 openpyxl 或 calamine 底层分页读,或者换思路:先用 pd.read_excel(..., nrows=1) 获取列名和 dtype,再用 polars 分块读(pl.read_excel(..., read_options={"n_rows": 50000})),最后转成 Pandas。
立即学习“Python免费学习笔记(深入)”;
- 最轻量的应急法:用
openpyxl.load_workbook(f, read_only=True)打开工作簿,取ws.max_row,再用ws.iter_rows(min_row=2, max_row=50000, values_only=True)手动读块,转pd.DataFrame - 如果允许引入 Polars:它原生支持 Excel 分块读 + 多文件并行,
pl.scan_excel(f).collect().to_pandas()能压低峰值内存 - 千万注意:不要在分块读时对每块都
pd.concat(),应攒够 N 块(比如 50 个文件 × 每块 1w 行 = 50w 行)再合并一次,减少对象创建频次


















