正确做法是先用pathlib批量读取xlsx文件到DataFrame列表,再用pd.concat一次性合并;需统一header、dtype、跳过临时文件,并处理列名不一致问题。

用 pandas.concat() 合并多个 .xlsx 文件,别直接 read_excel() 循环加 +
直接用 + 或 append()(已弃用)拼 DataFrame 会触发多次内存拷贝,10 个文件以上就明显变慢,还容易因列顺序不一致导致错位。正确做法是先收集所有 DataFrame 到列表,再一次性 concat()。
常见错误现象:ValueError: Plan shapes are not aligned,本质是某个文件列名少一列、或多了一列空列,或首行不是表头(比如带合并单元格的 Excel)。
- 先统一用
pd.read_excel(file, header=0)显式指定表头行,避免默认行为差异 - 读取时加
dtype=str或按需指定列类型,防止某文件某列出现数字/空值混合被自动转成float64,后续合并时报类型冲突 - 用
pd.concat(dfs, ignore_index=True, sort=False):其中ignore_index=True重置行号,sort=False避免 pandas 自动按列名排序(否则列顺序会被打乱)
路径匹配和文件遍历:别手写 for i in range(1, 100) 拼文件名
人工数文件编号极易漏、易错,且无法处理“report_20240401.xlsx”这类日期命名。应该用 glob 或 pathlib 批量发现文件。
使用场景:文件在同目录下;或需排除临时文件(如 ~$report.xlsx);或只读取特定前缀的文件。
立即学习“Python免费学习笔记(深入)”;
- 推荐用
pathlib.Path("data/").glob("*.xlsx"),比os.listdir()更安全,自动过滤隐藏文件和非 Excel 文件 - 加一层判断:
if not file.name.startswith("~$"),跳过 Excel 临时锁文件,否则read_excel()会报PermissionError或解析出空表 - 如果文件跨子目录,用
r.glob("**/*.xlsx"),但注意性能——太多嵌套层级会变慢
read_excel() 的关键参数:跳过空行、处理多表头、应对合并单元格
真实业务 Excel 常有标题栏空行、双层表头、第一行是单位说明等。硬编码 header=0 会导致列名变成 Unnamed: 0 或数据整体偏移。
错误现象:KeyError: '销售金额',其实列名是 '销售金额\n(万元)' 或前面多了几个空列。
- 用
skiprows跳过说明行,例如skiprows=2表示跳过前两行,第三行当表头 - 遇到双表头(如第 0 行是大类,第 1 行是小类),用
header=[0, 1],但后续必须用元组访问列,如df[("销售额", "Q1")]—— 这会显著增加后续清洗成本,建议先扁平化列名 - 合并单元格?
read_excel()默认会把合并单元格的值只填在首单元格,其余为空,这是正常行为,不是 bug;若需补全,得自己用ffill()处理该列
写入合并结果时:别用 to_excel() 默认参数覆盖原文件
新手常写 df_all.to_excel("merged.xlsx") 就以为完事了,结果打开发现格式全丢、列宽回退、日期变数字——因为 to_excel() 默认不保留任何 Excel 样式,且不启用 index=False 时还会多出一列索引。
性能影响:如果文件很大(>10 万行),用 openpyxl 引擎比默认 xlsxwriter 更兼容已有格式(但不能追加写入已有 sheet)。
- 务必加
index=False,除非你真需要行号作为一列数据 - 如需保留原始格式(字体、颜色、冻结窗格等),
pandas无能为力,得切到openpyxl手动操作 —— 这属于另一层复杂度,普通聚合不建议碰 - 写入前检查
df_all.shape,如果行数远小于预期(比如本该 5000 行只写出 50 行),大概率是某次read_excel()因空行或表头错位只读了表头没读数据
最麻烦的其实是列名不一致:A 文件叫 "客户ID",B 文件叫 "cust_id",C 文件叫 "客户编号"。这种没法靠代码自动对齐,得提前约定命名规范,或写映射字典做标准化——这一步最容易被跳过,结果合并完才发现字段对不上。


















