Python + xlwings 按分类字段拆分 Excel 总表为多个工作簿的实战脚本
在日常办公中,经常需要将一张包含多类数据的 Excel 总表,按照某个字段(如产品、地区、门店)拆分成多个独立的工作簿。如果手动操作,需要反复筛选、复制、新建、粘贴、保存,不仅效率低下,还容易出错。使用 Python 脚本可以一键完成这一任务,稳定且可复用。本文将基于 xlwings 库,详细讲解如何实现按条件拆分的完整流程,并提供可直接运行的代码。## 一、业务场景与核心理念
典型场景包括:销售总表按产品拆分给产品负责人,门店数据按门店拆分给店长,区域业绩按地区拆分给区域经理。只要表格中有明确的分类列(如“类别”、“地区”等),就可以用程序自动分组输出。
核心思路是使用 Python 的字典(dict)作为“分类容器”。字典的键(key)是分类字段的值,值(value)是该分类下的所有数据行(列表形式)。例如:
data = {
"背包": [
["双肩包", "背包", 10, 129, 1290],
["登山包", "背包", 5, 199, 995]
],
"行李箱": [
["拉杆箱", "行李箱", 8, 299, 2392]
]
}
然后遍历字典,对每个 key 生成一个独立的工作簿,将表头和数据行写入后保存。这样,分类字段的值就直接决定了输出文件名和内容。
## 二、完整代码:按指定列拆分总表
以下代码使用 xlwings(操作 Excel 的第三方库),读取源文件,按指定列(默认第 1 列,即 B 列)分组,并输出到目标文件夹。代码已处理文件名非法字符、空分类值、工作表名称长度限制等问题。
import os
import re
import xlwings as xw
def safe_filename(name):
"""将分类值转换为合法文件名,替换非法字符为下划线"""
name = str(name).strip()
name = re.sub(r'[\\/:*?"<>|]', "_", name)
return name if name else "未分类"
# ====== 需要根据实际情况修改的参数 ======
source_file = r"e:\file\总表.xlsx"
source_sheet = "Sheet1"
output_dir = r"e:\file\拆分结果"
group_col_index = 1# 按哪一列拆分:0 表示 A 列,1 表示 B 列,以此类推
# =====================================
os.makedirs(output_dir, exist_ok=True)
app = xw.App(visible=False, add_book=False)
try:
wb = app.books.open(source_file)
sht = wb.sheets
# 读取连续区域(包含表头)
table = sht.range("A1").expand("table").value
if not table or len(table) < 2:
raise ValueError("源表数据为空或只有表头,没有可拆分的数据行。")
header = table
rows = table
data = dict()
for row in rows:
key = row
if key is None or str(key).strip() == "":
key = "未分类"
key = str(key).strip()
if key not in data:
data = []
data.append(row)
for key, value in data.items():
file_name = safe_filename(key) + ".xlsx"
out_path = os.path.join(output_dir, file_name)
new_wb = app.books.add()
new_sht = new_wb.sheets
new_sht.name = safe_filename(key)[:31]# Excel 工作表名最长 31 字符
new_sht.range("A1").value = + value# 表头 + 数据行
new_wb.save(out_path)
new_wb.close()
print(f"已生成:{out_path},行数:{len(value)}")
wb.close()
finally:
app.quit()
## 三、关键代码解析
- **data = dict()**:创建一个空字典,用于存放分组结果。
- **if key not in data: data = []**:如果该分类尚未出现,则初始化空列表。
- **data.append(row)**:将当前行追加到对应分类列表。
- ** + value**:将表头与数据行拼接,确保输出文件包含字段名称。
- **safe_filename(key)**:去除分类值中的空格,并将 Windows 非法字符(\ / : * ? " < > |)替换为下划线,避免保存失败。
- **new_sht.name = safe_filename(key)[:31]**:Excel 工作表名称长度不能超过 31 个字符,截断处理。
## 四、常见问题与检查要点
1. **分类字段为空**:脚本中已将 None 或空白行归入“未分类”,但实际业务中可能需要特殊处理(如跳过或报错)。
2. **分类值存在前后空格**:使用 strip() 去除空格,否则“华北”和“华北 ”会被视为两个不同分类。
3. **文件名重复冲突**:若多个分类清洗后得到相同文件名,后生成的会覆盖先前的。建议使用唯一编号避免覆盖:
def get_unique_path(folder, filename):
base, ext = os.path.splitext(filename)
path = os.path.join(folder, filename)
index = 1
while os.path.exists(path):
path = os.path.join(folder, f"{base}_{index}{ext}")
index += 1
return path
4. **输出目录不存在**:使用 os.makedirs(output_dir, exist_ok=True) 自动创建。
## 五、结果验证:确保拆分正确
运行脚本后,不能只看控制台无报错。应检查以下三点:
- 输出文件数量是否等于分类数量。
- 每个工作簿中的分类列数据是否一致(抽样检查)。
- 输出数据行总数是否等于源表数据行数。
可以使用以下代码快速核对:
total_output_rows = 0
for key, value in data.items():
print(f"{key}:{len(value)} 行")
total_output_rows += len(value)
print(f"源数据行数:{len(rows)}")
print(f"输出数据行数合计:{total_output_rows}")
原理:按条件拆分且不删除数据,输出行数总和应等于源表数据行数。若不一致,需排查空值处理、过滤逻辑或数据读取范围。
## 六、实战总结
本文展示的“读取→分组→逐组输出”模型,可广泛应用于销售数据分发、资产清单拆分、人员绩效报表等场景。关键是先确认分类字段的可靠性,在输出前做数据清洗(去空格、处理空值、文件名合法化),最后对结果进行核对。将此脚本进一步封装,可加入图形界面或配置参数,成为稳定的办公自动化工具。
Re: Python + xlwings 按分类字段拆分 Excel 总表为多个工作簿的实战脚本
非常实用的脚本,感谢分享!处理 Excel 拆分这个需求确实很常见,手动操作费时又容易出错。代码里对非法字符、空分类值、工作表名称长度限制这些细节都考虑到了,很严谨。字典分组 + xlwings 直接操作 Excel 应用的思路也很清晰,不用依赖临时文件,保存路径可以灵活指定,直接就能拿到结果。另外,建议可以在拆分前加个校验,看看分类列索引是否超出数据范围,或者表头是否包含预期的字段名,能防一手源表结构变化。总之这套模板可以改成带界面或者命令行参数的小工具,日常办公会非常方便。Re: Python + xlwings 按分类字段拆分 Excel 总表为多个工作簿的实战脚本
感谢分享,非常实用的脚本!代码结构清晰,注释也很到位,特别是对文件名字符处理和Excel工作表长度限制的考虑很周全,直接拿来修改参数就能用,省去了手动反复操作的麻烦。我通常用pandas处理这类拆分,但xlwings这种方式能直接保留原格式和公式,更贴合实际办公场景。想请教一下,如果总表数据量非常大(比如几十万行),内存占用方面有什么需要注意的吗?或者有没有批量分页保存的建议?再次感谢!Re: Python + xlwings 按分类字段拆分 Excel 总表为多个工作簿的实战脚本
感谢分享!这个脚本非常实用,尤其是 `safe_filename` 和键判断那部分的边界处理,考虑得很周到。我平时也用 xlwings 处理 Excel,但没想过把字典的键值对设计成“分类容器”,这个思路很清晰。 有个小建议:如果原表数据量很大(比如超过几万行),循环里逐行 `data.append(row)` 可能稍慢,或许可以用 `pandas` 的 `groupby` 先分组再转列表,不过依赖库就多了。另外 `data = []` 那行有点笔误,应该是 `data = []` 吧? 总之代码可以直接复用,对办公自动化很友好。感谢分享!
页:
[1]