[Python实战] 一堆 Excel 表要合并,怎样先把字段、文件名和异常行管起来?
很多人第一次用 Python 合并一堆 Excel 表时,最容易犯的错误不是代码写错,而是太早开始合并。
目录里有几十个文件,看起来结构差不多,字段名也差不多,于是直接写一个循环,把每个表读进来再 concat。代码可能很快跑完,但结果常常不可信:有些列其实不是同一个含义,有些文件名没有说明来源,有些异常行被混进总表,最后做统计时才发现口径已经乱了。
所以,批量合并 Excel 的第一步不应该是写合并代码,而是先把三个问题管起来:字段、文件名、异常行。
一、先别急着合并,先确认每个文件代表什么
批量文件最怕来源不清。
比如一个目录里有这些文件:
销售明细-北京-2026-06.xlsx
销售明细-上海-2026-06.xlsx
销售明细-广州-2026-06.xlsx
临时汇总.xlsx
旧版备份.xlsx
如果脚本只是把所有 .xlsx 都读进去,临时汇总和旧版备份也可能被合并进去。最后行数变多了,但多出来的不是业务数据,而是脏文件。
更好的做法是先定义文件名规则。
例如:
文件名必须包含城市
文件名必须包含月份
文件名必须以销售明细开头
不符合规则的文件先进入异常清单
一个最小检查可以这样写:
from pathlib import Path
import re
folder = Path('data/excel')
pattern = re.compile(r'^销售明细-(?P<city>.+)-(?P<month>\d{4}-\d{2})\.xlsx$')
valid_files = []
invalid_files = []
for path in folder.glob('*.xlsx'):
match = pattern.match(path.name)
if match:
valid_files.append((path, match.groupdict()))
else:
invalid_files.append(path.name)
print('可合并文件:', len(valid_files))
print('异常文件:', invalid_files)
这段代码不负责合并,只负责回答一个问题:哪些文件有资格进入合并流程。
二、字段不要凭感觉对齐,要先做字段清单
Excel 表最常见的问题是字段名看起来相似,但实际并不一致。
比如:
客户名称
客户名
客户
公司名称
再比如:
销售金额
成交金额
金额
订单金额
如果直接合并,pandas 会把它们当成不同列。结果是总表里列越来越多,很多地方出现空值,看起来像数据缺失,其实是字段没有对齐。
合并前应该先扫描每个文件的字段。
import pandas as pd
field_report = []
for path, meta in valid_files:
df_head = pd.read_excel(path, nrows=0)
field_report.append({
'file': path.name,
'city': meta['city'],
'month': meta['month'],
'columns': list(df_head.columns),
})
for item in field_report:
print(item['file'], item['columns'])
这一步的价值很大。它能提前暴露哪些文件字段不一致,而不是等合并后再从一堆空值里找原因。
三、建立字段映射,而不是在代码里到处写 if
字段不一致时,不建议在合并循环里写很多临时判断。
更清楚的方式是维护一张字段映射表。
FIELD_MAP = {
'客户名称': '客户名称',
'客户名': '客户名称',
'客户': '客户名称',
'公司名称': '客户名称',
'销售金额': '销售金额',
'成交金额': '销售金额',
'订单金额': '销售金额',
'金额': '销售金额',
}
TARGET_COLUMNS = ['客户名称', '销售金额', '城市', '月份']
然后在读取每个文件后统一重命名:
df = pd.read_excel(path)
df = df.rename(columns={c: FIELD_MAP.get(c, c) for c in df.columns})
这比在每个文件上临时修列名更可控。以后新增一种字段写法,只需要改字段映射表,不需要翻整段合并代码。
四、来源信息要写进表里,不要只留在文件名里
合并后的总表里,最好保留来源字段。
比如:
source_file
city
month
否则后面发现某些行异常时,很难追溯它来自哪个文件。
df['source_file'] = path.name
df['城市'] = meta['city']
df['月份'] = meta['month']
这一步看起来简单,但它决定了后面能不能排查问题。
很多合并脚本之所以不好维护,就是因为合并之后所有来源信息都丢了。等业务同事问“这几行是哪里来的”,脚本作者只能重新翻原始文件。
五、异常行不要悄悄丢掉,要单独输出
合并时经常会遇到这些异常:
客户名称为空
金额为空
金额不是数字
整行都是空值
表头位置不一致
文件里混入了说明行
这些行不应该直接丢掉,也不应该混进总表。更好的做法是把它们单独放到异常表。
required = ['客户名称', '销售金额']
valid_rows = df.dropna(subset=required)
invalid_rows = df[df[required].isna().any(axis=1)].copy()
invalid_rows['error_reason'] = '关键字段为空'
这样最终可以输出两个文件:
merged.xlsx:可用总表
errors.xlsx:异常明细
这比只输出一个总表更适合真实工作,因为异常数据往往需要人工确认。
六、合并前先生成检查报告
如果文件很多,建议在真正合并前先生成一个检查报告。
报告里至少包括:
总文件数
符合命名规则的文件数
异常文件数
每个文件的字段列表
缺失字段的文件
异常行数量
最终可合并行数
一个简单结构可以是:
report = {
'total_files': len(list(folder.glob('*.xlsx'))),
'valid_files': len(valid_files),
'invalid_files': invalid_files,
'field_report': field_report,
}
实际项目里,我更建议把报告输出成 JSON 或 Excel。因为它不是给脚本看的,而是给后续排查和沟通用的。
七、一个更完整的合并流程
把上面的步骤串起来,流程应该是:
第一步,扫描目录,只接收符合命名规则的文件。
第二步,读取每个文件的表头,生成字段清单。
第三步,根据字段映射表统一列名。
第四步,把文件名里的业务信息写入数据列。
第五步,拆分正常行和异常行。
第六步,输出合并总表、异常明细和检查报告。
第七步,再让人确认异常明细是否需要补录或修正。
这个顺序比“直接合并再说”慢一点,但结果更可信。
八、什么时候才适合写成正式工具
如果只是偶尔合并一次,写一个脚本就够了。
但如果这件事每周、每月都要做,就应该把字段映射、文件名规则、输出路径都抽到配置里。
比如:
{
"filename_pattern": "^销售明细-(?P<city>.+)-(?P<month>\\d{4}-\\d{2})\\.xlsx$",
"target_columns": ["客户名称", "销售金额", "城市", "月份"],
"required_columns": ["客户名称", "销售金额"]
}
这样脚本就不只是一次性代码,而是一个小型的数据整理工具。
九、真正影响效率的不是合并,而是合并前的治理
Python 合并 Excel 本身并不难。真正难的是让合并结果可以被信任。
字段不统一,合并出来的表会乱。
文件名不规范,来源会乱。
异常行不处理,统计结果会乱。
所以,批量合并 Excel 的核心不是 concat,而是合并前的检查、映射、追溯和异常处理。只要这几件事先管住,后面的自动化才不会越做越难排查。

更多推荐

所有评论(0)