在日常办公中,经常需要将分散在不同子文件夹下的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[desired_columns]
复制代码
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读取、列名映射等功能。 |