Python实战:os.walk与pandas合并多文件夹Excel至总表(附完整代码)
在日常办公中,经常需要将分散在不同子文件夹下的Excel文件汇总到一个总表中。手动打开每个文件复制粘贴不仅耗时,而且容易出错。本文提供一个纯Python脚本解决方案,利用os.walk递归遍历目录、pandas读取Excel并纵向拼接,实现一键合并。整个过程不到30行代码,3秒即可完成。技术原理
核心逻辑分三步:
1. 使用os.walk()递归扫描所有子文件夹,找出所有.xlsx和.xls文件。
2. 用pandas.read_excel()将每个Excel文件读取为一个DataFrame。
3. 用pandas.concat()将所有DataFrame按行合并,最后导出为新的Excel文件。
环境准备
需要安装pandas和openpyxl模块(openpyxl是pandas读取.xlsx文件的引擎):
pip install pandas openpyxl
完整代码
以下脚本定义了函数merge_excel_files,接收源文件夹路径和输出文件名,自动合并所有Excel。
import os
import pandas as pd
from pathlib import Path
def merge_excel_files(source_dir, output_file="总表汇总.xlsx"):
all_dataframes = []
file_count = 0
root_path = Path(source_dir)
if not root_path.exists():
print(f"错误:目录 {source_dir} 不存在!")
return
for dirpath, dirnames, filenames in os.walk(source_dir):
for filename in filenames:
if filename.endswith(('.xlsx', '.xls')) and not filename.startswith('~$'):
file_path = os.path.join(dirpath, filename)
file_count += 1
try:
engine = 'openpyxl' if filename.endswith('.xlsx') else 'xlrd'
df = pd.read_excel(file_path, engine=engine)
df['数据来源文件'] = filename
df['数据来源路径'] = dirpath
all_dataframes.append(df)
print(f"[{file_count}] 已读取: {filename}")
except Exception as e:
print(f"[{file_count}] 读取失败 {filename}: {e}")
if not all_dataframes:
print("没有找到任何 Excel 文件!")
return
print("\n正在合并所有数据...")
merged_df = pd.concat(all_dataframes, ignore_index=True)
merged_df.to_excel(output_file, index=False, engine='openpyxl')
print(f"\n合并完成!共处理 {file_count} 个文件,总数据行数: {len(merged_df)}")
print(f"汇总文件已保存: {output_file}")
if __name__ == "__main__":
SOURCE_DIR = r"D:\数据源"
merge_excel_files(SOURCE_DIR, output_file="总表汇总.xlsx")
代码功能分解
1. 遍历文件夹
os.walk(source_dir)递归返回当前目录路径、子文件夹列表、文件列表。对于每个文件,只用endswith检查扩展名,并跳过~$开头的临时文件。
2. 读取Excel并标记来源
pd.read_excel()默认读取第一个工作表,engine自动选择openpyxl或xlrd。添加“数据来源文件”和“数据来源路径”两列方便追溯原始出处。
3. 合并与导出
pd.concat()将所有DataFrame纵向拼接,ignore_index=True重置行索引避免冲突。to_excel()保存时不导出默认行号。
进阶技巧
技巧1:指定读取的Sheet名称
如果Excel文件有多个Sheet,可以传入sheet_name参数:
df = pd.read_excel(file_path, sheet_name="Sheet1", engine='openpyxl')
或者读取所有Sheet:
all_sheets = pd.read_excel(file_path, sheet_name=None, engine='openpyxl')
for sheet_name, sheet_df in all_sheets.items():
sheet_df['来源Sheet'] = sheet_name
all_dataframes.append(sheet_df)
技巧2:统一列名后再合并
当不同文件的列名不完全一致时,可以先定义映射字典并重命名列:
COLUMN_MAP = {
'姓名': '姓名',
'员工姓名': '姓名',
'Name': '姓名',
'部门': '部门',
'所属部门': '部门',
'金额': '金额',
'金额(元)': '金额',
}
df = pd.read_excel(file_path)
df.rename(columns=COLUMN_MAP, inplace=True)
技巧3:大文件进度条
如果文件数量很多,可以安装tqdm并包装遍历循环:
from tqdm import tqdm
for dirpath, dirnames, filenames in tqdm(os.walk(source_dir), desc="扫描文件夹"):
for filename in filenames:
# 处理逻辑不变
常见问题
Q1:ModuleNotFoundError: No module named 'openpyxl'
原因:缺少openpyxl库。pip install openpyxl即可解决。
Q2:合并后列的顺序乱了怎么办?
pd.concat会自动按字母顺序排列列名(pandas 1.3+),可以手动指定顺序:
desired_columns = ['姓名', '部门', '金额', '日期', '数据来源文件', '数据来源路径']
merged_df = merged_df
Q3:某些Excel读取为空?
可能是文件只有表头无数据,或被加密,或实际是.csv改后缀。建议读取后判断:
if df.empty:
print(f"警告: {filename} 为空,跳过")
continue
Q4:如何合并.csv文件?
将读取改为pd.read_csv(),并注意编码:
if filename.endswith('.csv'):
df = pd.read_csv(file_path, encoding='utf-8-sig')
总结
通过os.walk()遍历、pd.read_excel()读取、pd.concat()合并、to_excel()导出,一整套Excel自动化合并脚本即可完成。此方法适用于财务报表、销售数据、调查问卷等格式一致的批量汇总,显著提升工作效率。注意依赖库的安装和文件格式的兼容性,可依实际需求扩展Sheet读取、列名映射等功能。
Re: Python实战:os.walk与pandas合并多文件夹Excel至总表(附完整代码)
楼主这个脚本很实用啊,正好解决我平时汇总报表的痛点。代码结构清晰,注释也很到位,尤其是加了来源路径和文件名这两列,方便追查数据来源。另外那个统一列名的技巧也很有用,实际工作中不同部门的表格列名确实经常不统一。想请教一下,如果文件数量特别多(比如几百个)或者单个文件很大,有没有什么性能方面的优化建议?比如用分块读取或者多线程?感谢分享!Re: Python实战:os.walk与pandas合并多文件夹Excel至总表(附完整代码)
感谢分享!代码写得非常清晰,尤其是自动跳过临时文件和添加来源路径的设计很实用。我平时也经常用 `os.walk` 配合 pandas 做数据合并,但没考虑过列名映射的统一问题,你提到的技巧2正好能解决我常碰到的字段名不一致的痛点。另外,如果文件数量特别多,有没有考虑过用 `glob` 替代 `os.walk` 做更简单的筛选?或者用 `tqdm` 加个进度条会更直观。总之收藏了,以后直接拿来用!Re: Python实战:os.walk与pandas合并多文件夹Excel至总表(附完整代码)
感谢分享,写得非常详细实用!处理多个文件夹下的Excel合并确实是日常办公的高频需求,这份脚本直接拿来就能用。 想补充一个小建议:如果数据量特别大(比如几百个Excel文件或每个文件有几十万行),`pandas.concat`一次性合并可能会占用较多内存。可以考虑分批写入Excel——先创建一个空的Excel Writer,逐步追加写入每个DataFrame,这样能降低内存峰值。另外,如果有些Excel文件的实际内容为空(只有表头),读进来会报错或产生空DataFrame,可以在`try`里加一条`if df.empty: continue`跳过,避免后续拼接时出现问题。 代码里已经包含了跳过临时文件和添加来源信息的细节,很贴心。再次感谢分享!
页:
[1]