场景痛点:
每天被几十个结构相同的Excel报表折磨?手动打开→复制→粘贴→关闭,循环到怀疑人生?尤其月底汇总时,眼睛都快看瞎了。今天教你3秒合并上百个Excel,合完直接下班!
核心依赖库:
-
pandas:数据处理核心
-
openpyxl:读写.xlsx文件(pandas底层调用)
-
os:文件路径操作
-
glob:批量匹配文件名(比os.listdir更优雅)
完整代码(带保姆级注释)
import pandas as pd
import os
import glob
from datetime import datetime
# ==================== 配置区(请按需修改)====================
# 1. 设置存放Excel的文件夹路径(建议用绝对路径,避免迷路)
FOLDER_PATH = r"C:\\Users\\你的用户名\\Desktop\\待合并报表"
# 2. 要合并的文件后缀(支持 .xlsx 和 .xls)
FILE_EXTENSION = "*.xlsx"
# 3. 输出文件名(自动加时间戳,防止覆盖)
OUTPUT_FILE = f"合并结果_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
# 4. 是否保留原始文件名作为来源标记(方便追溯)
ADD_SOURCE_COLUMN = True # True=新增一列"来源文件"
# ============================================================
def merge_excel_files(folder_path, ext, output, add_source):
"""
合并指定文件夹下所有同结构Excel文件
"""
# 构建文件匹配路径
search_path = os.path.join(folder_path, ext)
file_list = glob.glob(search_path)
# 检查是否找到文件
if not file_list:
print(f"❌ 在 {folder_path} 下未找到任何 {ext} 文件,请检查路径!")
return
print(f"✅ 找到 {len(file_list)} 个待合并文件:")
for f in file_list:
print(f" – {os.path.basename(f)}")
# 用列表收集所有DataFrame(比逐行concat更高效)
df_list = []
for file_path in file_list:
try:
# 读取Excel(只读数据,忽略样式和格式)
df = pd.read_excel(file_path, dtype=str) # dtype=str防止数字变科学计数法
# 可选:添加来源列
if add_source:
df.insert(0, "来源文件", os.path.basename(file_path)) # 插到第一列
df_list.append(df)
print(f" ✓ 已读取: {os.path.basename(file_path)} (行数: {len(df)})")
except Exception as e:
print(f" ⚠️ 读取失败 {os.path.basename(file_path)}: {e}")
continue
# 合并所有DataFrame
if df_list:
merged_df = pd.concat(df_list, ignore_index=True)
# 保存结果(不包含索引列)
merged_df.to_excel(output, index=False)
print(f"\\n🎉 合并完成!共 {len(merged_df)} 行数据")
print(f"📁 保存路径: {os.path.abspath(output)}")
else:
print("❌ 没有成功读取任何文件,请检查文件是否损坏或格式异常")
# ==================== 执行入口 ====================
if __name__ == "__main__":
merge_excel_files(FOLDER_PATH, FILE_EXTENSION, OUTPUT_FILE, ADD_SOURCE_COLUMN)
运行效果示例
控制台输出:
✅ 找到 5 个待合并文件:
– 销售日报_20260101.xlsx
– 销售日报_20260102.xlsx
– 销售日报_20260103.xlsx
– 销售日报_20260104.xlsx
– 销售日报_20260105.xlsx
✓ 已读取: 销售日报_20260101.xlsx (行数: 152)
✓ 已读取: 销售日报_20260102.xlsx (行数: 148)
✓ 已读取: 销售日报_20260103.xlsx (行数: 163)
✓ 已读取: 销售日报_20260104.xlsx (行数: 157)
✓ 已读取: 销售日报_20260105.xlsx (行数: 159)
🎉 合并完成!共 779 行数据
📁 保存路径: C:\\Users\\你的用户名\\Desktop\\待合并报表\\合并结果_20260123_153045.xlsx
避坑指南(老司机经验)
| 表头不一致 | 若列名不同,合并后会出现多列空值。先手动统一表头,或在代码中用df.columns = ['列1','列2']强制对齐 |
| 数字变成科学计数法 | 读取时加dtype=str,合并后如需计算再转数值(用pd.to_numeric) |
| 隐藏的.xlsx临时文件 | 打开Excel时产生的~$开头的临时文件会被读取报错,可以在glob匹配时过滤:if not file_path.startswith('~$') |
| 路径含中文 | 务必在路径字符串前加r(原始字符串),如r"C:\\我的报表",避免转义问题 |
| 内存爆炸 | 如果文件超大(单文件>100MB),改用pd.read_excel(…, usecols=['列1','列2'])只读必要列,或改用dask库分块处理 |
扩展建议(改造成其他场景)
1. 只合并指定Sheet(例如每个文件的“Sheet1”)
df = pd.read_excel(file_path, sheet_name="Sheet1", dtype=str)
2. 合并时跳过前3行(标题从第4行开始)
df = pd.read_excel(file_path, skiprows=3, dtype=str)
3. 合并后自动按“日期”列排序
merged_df['日期'] = pd.to_datetime(merged_df['日期'])
merged_df = merged_df.sort_values('日期')
4. 如果文件是CSV格式(更常见于数据导出)
df = pd.read_csv(file_path, encoding='utf-8-sig')
5. 打包成带图形界面的小工具(给不懂代码的同事用)
用tkinter做个文件夹选择窗口,一键执行 → 同事直呼大神
💡 最后说句掏心窝的话
这个脚本我用了快5年,从月薪8k到现在的架构师,它帮我省下的时间至少200小时。你今晚跑通后,明天上班就可以当着领导的面优雅地点一下运行,然后端起咖啡——放心,稳如老狗。
如果运行报错,复制完整报错信息贴出来,我帮你在线把脉! 🐍💊



