Python 自动化 Excel 合并 / 拆分单元格详细教程
在制作报表、台账、汇总表的时候,合并单元格是很常用的功能,能让表格结构更清晰美观。Python可以轻松实现单元格的合并与拆分,批量处理效率远超手动操作。本文将详细介绍各种合并/拆分场景的实现方法,包含完整代码示例和最佳实践。
一、环境准备
1.1 安装依赖库
pip install openpyxl pandas
1.2 注意事项
1. 合并单元格后会导致部分单元格数据丢失,合并前确保数据已经正确整理
2. 拆分单元格后需要补充数据,避免出现空值
3. 包含合并单元格的表格在排序、筛选、计算时会出现问题,建议数据处理完成后再合并
4. 合并单元格不要过度使用,会增加后续数据处理的难度
二、合并单元格基础操作
2.1 合并指定范围的单元格
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
wb = Workbook()
ws = wb.active
# 合并A1到F1单元格作为大标题
ws.merge_cells("A1:F1")
# 给合并后的单元格赋值(赋值给合并区域的左上角单元格即可)
ws["A1"] = "2026年第一季度销售报表"
# 设置样式
title_font = Font(name="微软雅黑", size=16, bold=True)
title_alignment = Alignment(horizontal="center", vertical="center")
ws["A1"].font = title_font
ws["A1"].alignment = title_alignment
# 设置行高
ws.row_dimensions[1].height = 30
# 合并表头:A2到B2为"基本信息",C2到F2为"销售数据"
ws.merge_cells("A2:B2")
ws["A2"] = "基本信息"
ws.merge_cells("C2:F2")
ws["C2"] = "销售数据"
# 表头样式
header_font = Font(bold=True, color="FFFFFF")
header_fill = PatternFill(start_color="3498db", end_color="3498db", fill_type="solid")
header_alignment = Alignment(horizontal="center", vertical="center")
for cell in ["A2", "C2"]:
ws[cell].font = header_font
ws[cell].fill = header_fill
ws[cell].alignment = header_alignment
# 写入子表头
sub_headers = ["日期", "区域", "产品A", "产品B", "产品C", "合计"]
for col, header in enumerate(sub_headers, 1):
cell = ws.cell(row=3, column=col, value=header)
cell.font = header_font
cell.fill = header_fill
cell.alignment = header_alignment
# 写入测试数据
data = [
["2026-01", "华东", 12000, 8500, 9800, "=SUM(C4:E4)"],
["2026-02", "华南", 15000, 9200, 10500, "=SUM(C5:E5)"],
["2026-03", "华北", 13500, 7800, 11200, "=SUM(C6:E6)"]
]
for row_idx, row_data in enumerate(data, 4):
for col_idx, value in enumerate(row_data, 1):
ws.cell(row=row_idx, column=col_idx, value=value)
# 调整列宽
for col in ["A", "B", "C", "D", "E", "F"]:
ws.column_dimensions[col].width = 15
# 保存文件
wb.save("合并单元格示例.xlsx")
print("✅ 合并单元格完成!")
2.2 按行号列号合并
除了用Excel坐标,还可以用行号列号合并:
from openpyxl import Workbook
wb = Workbook()
ws = wb.active
# 合并第1行,第1列到第6列(等价于A1:F1)
ws.merge_cells(start_row=1, start_column=1, end_row=1, end_column=6)
ws["A1"] = "销售报表"
# 合并第2-4行,第1列(等价于A2:A4)
ws.merge_cells(start_row=2, start_column=1, end_row=4, end_column=1)
ws["A2"] = "第一季度"
wb.save("按坐标合并.xlsx")
2.3 合并多个不连续的单元格
from openpyxl import Workbook
wb = Workbook()
ws = wb.active
# 要合并的多个单元格范围
merge_ranges = [
"A1:C1", # 大标题
"A2:A4", # 区域列
"D2:F2", # 数据标题
"A5:C5", # 合计行标题
]
for range_str in merge_ranges:
ws.merge_cells(range_str)
# 赋值
ws["A1"] = "销售数据汇总"
ws["A2"] = "华东区"
ws["D2"] = "销售明细"
ws["A5"] = "合计"
wb.save("多区域合并.xlsx")
print("✅ 多个区域合并完成!")
三、常见合并场景实战
3.1 相同内容自动合并
把相同内容的相邻单元格自动合并,比如相同区域、相同月份的行:
from openpyxl import load_workbook
wb = load_workbook("销售数据.xlsx")
ws = wb.active
# 按区域列(B列)合并相同区域的单元格
# 先找出相同内容的连续行
start_row = 2 # 从第二行开始(跳过表头)
current_value = ws.cell(row=start_row, column=2).value
for row in range(3, ws.max_row + 1):
value = ws.cell(row=row, column=2).value
if value != current_value:
# 和上一个值不同,合并前面的连续行
if start_row < row - 1:
ws.merge_cells(start_row=start_row, start_column=2, end_row=row-1, end_column=2)
print(f"✅ 合并B{start_row}:B{row-1},内容:{current_value}")
start_row = row
current_value = value
# 合并最后一组
if start_row < ws.max_row:
ws.merge_cells(start_row=start_row, start_column=2, end_row=ws.max_row, end_column=2)
print(f"✅ 合并B{start_row}:B{ws.max_row},内容:{current_value}")
wb.save("销售数据_合并相同区域.xlsx")
3.2 制作台账表头合并
制作复杂的多级表头,非常适合报表:
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, PatternFill
wb = Workbook()
ws = wb.active
# 第一级表头(合并整个第一行)
ws.merge_cells("A1:I1")
ws["A1"] = "XX公司员工信息台账"
ws["A1"].font = Font(size=18, bold=True)
ws["A1"].alignment = Alignment(horizontal="center", vertical="center")
ws.row_dimensions[1].height = 35
# 第二级表头
ws.merge_cells("A2:B2")
ws["A2"] = "基本信息"
ws.merge_cells("C2:E2")
ws["C2"] = "工作信息"
ws.merge_cells("F2:H2")
ws["F2"] = "薪资信息"
ws.merge_cells("I2:I3")
ws["I2"] = "备注"
# 第三级表头
headers = [
"姓名", "部门", "入职日期", "岗位", "职级",
"基本工资", "绩效工资", "补贴"
]
for col, header in enumerate(headers, 1):
cell = ws.cell(row=3, column=col, value=header)
cell.alignment = Alignment(horizontal="center", vertical="center")
# 表头样式
header_fill = PatternFill(start_color="3498db", end_color="3498db", fill_type="solid")
white_font = Font(color="FFFFFF", bold=True)
for row in [2, 3]:
for col in range(1, 10):
cell = ws.cell(row=row, column=col)
cell.fill = header_fill
cell.font = white_font
# 调整列宽
for col in ["A", "B", "C", "D", "E", "F", "G", "H", "I"]:
ws.column_dimensions[col].width = 15
wb.save("员工台账表头.xlsx")
print("✅ 台账多级表头合并完成!")
3.3 合并居中所有表头
批量处理多个工作表的表头合并居中:
from openpyxl import load_workbook
from openpyxl.styles import Alignment
wb = load_workbook("多工作表报表.xlsx")
center_alignment = Alignment(horizontal="center", vertical="center")
for ws in wb.worksheets:
# 合并第一行作为标题
if ws.max_column > 1:
ws.merge_cells(start_row=1, start_column=1, end_row=1, end_column=ws.max_column)
ws.cell(row=1, column=1).alignment = center_alignment
print(f"✅ 已合并工作表{ws.title}的表头")
wb.save("多工作表报表_表头合并.xlsx")
四、拆分单元格
4.1 拆分指定合并单元格
from openpyxl import load_workbook
wb = load_workbook("合并单元格示例.xlsx")
ws = wb.active
# 查看所有合并单元格
print("当前合并单元格:")
for merged_range in ws.merged_cells.ranges:
print(f" - {merged_range}")
# 拆分指定单元格范围
ws.unmerge_cells("A1:F1")
print("✅ 已拆分A1:F1")
# 拆分所有合并单元格
for merged_range in list(ws.merged_cells.ranges):
ws.unmerge_cells(str(merged_range))
print(f"✅ 已拆分{merged_range}")
wb.save("拆分单元格示例.xlsx")
4.2 拆分后自动填充数据
拆分单元格后,原来只有左上角单元格有数据,其他单元格为空,可以自动填充:
from openpyxl import load_workbook
wb = load_workbook("销售数据_合并相同区域.xlsx")
ws = wb.active
# 先获取所有合并单元格信息
merged_info = []
for merged_range in ws.merged_cells.ranges:
# 获取合并范围和值
min_row, min_col, max_row, max_col = merged_range.bounds
value = ws.cell(row=min_row, column=min_col).value
merged_info.append((min_row, min_col, max_row, max_col, value))
# 拆分所有合并单元格
for merged_range in list(ws.merged_cells.ranges):
ws.unmerge_cells(str(merged_range))
# 填充拆分后的空单元格
for min_row, min_col, max_row, max_col, value in merged_info:
for row in range(min_row, max_row + 1):
for col in range(min_col, max_col + 1):
ws.cell(row=row, column=col, value=value)
print(f"✅ 已拆分并填充{min_row},{min_col}到{max_row},{max_col},值:{value}")
wb.save("拆分后填充数据.xlsx")
print("✅ 拆分并填充完成!")
4.3 批量拆分所有Excel文件中的合并单元格
import os
from openpyxl import load_workbook
folder_path = "报表文件/"
for filename in os.listdir(folder_path):
if filename.endswith(".xlsx") and not filename.startswith("~$"):
file_path = os.path.join(folder_path, filename)
print(f"正在处理:{filename}")
wb = load_workbook(file_path)
for ws in wb.worksheets:
# 获取所有合并单元格信息
merged_info = []
for merged_range in ws.merged_cells.ranges:
min_row, min_col, max_row, max_col = merged_range.bounds
value = ws.cell(row=min_row, column=min_col).value
merged_info.append((min_row, min_col, max_row, max_col, value))
# 拆分
for merged_range in list(ws.merged_cells.ranges):
ws.unmerge_cells(str(merged_range))
# 填充
for min_row, min_col, max_row, max_col, value in merged_info:
for row in range(min_row, max_row + 1):
for col in range(min_col, max_col + 1):
ws.cell(row=row, column=col, value=value)
new_filename = f"拆分_{filename}"
wb.save(os.path.join(folder_path, new_filename))
print(f"✅ 处理完成,保存为:{new_filename}")
print("✅ 批量拆分完成!")
五、合并/拆分高级技巧
5.1 合并单元格后保留所有数据
默认合并单元格只会保留左上角的数据,如果需要合并后显示所有数据,可以先把内容合并:
from openpyxl import load_workbook
wb = load_workbook("待合并文件.xlsx")
ws = wb.active
# 合并A2:A5单元格,把所有内容用换行符连接
content = []
for row in range(2, 6):
value = ws.cell(row=row, column=1).value
if value:
content.append(str(value))
merged_content = "\n".join(content)
# 合并单元格
ws.merge_cells("A2:A5")
ws["A2"] = merged_content
# 设置自动换行
ws["A2"].alignment = Alignment(wrap_text=True, vertical="center")
# 调整行高
ws.row_dimensions[2].height = len(content) * 15
wb.save("合并并保留内容.xlsx")
print("✅ 合并完成,已保留所有内容")
5.2 按条件动态合并
根据数据内容动态决定是否合并:
from openpyxl import Workbook
wb = Workbook()
ws = wb.active
# 示例数据
data = [
["日期", "区域", "销售额"],
["2026-01-01", "华东", 12000],
["2026-01-01", "华东", 8500],
["2026-01-01", "华南", 9800],
["2026-01-02", "华东", 15000],
["2026-01-02", "华北", 11000],
]
# 写入数据
for row in data:
ws.append(row)
# 按日期列合并相同日期的单元格
start_row = 2
current_date = ws["A2"].value
for row in range(3, ws.max_row + 1):
date = ws[f"A{row}"].value
if date != current_date:
if start_row < row - 1:
ws.merge_cells(f"A{start_row}:A{row-1}")
print(f"✅ 合并日期{current_date}的单元格")
start_row = row
current_date = date
# 合并最后一组
if start_row < ws.max_row:
ws.merge_cells(f"A{start_row}:A{ws.max_row}")
print(f"✅ 合并日期{current_date}的单元格")
# 设置居中
for merged_range in ws.merged_cells.ranges:
min_row, min_col, _, _ = merged_range.bounds
ws.cell(row=min_row, column=min_col).alignment = Alignment(horizontal="center", vertical="center")
wb.save("按日期合并.xlsx")
5.3 合并跨列居中(不合并单元格)
有时候我们只是想要内容跨列居中显示,并不想真的合并单元格(避免影响后续数据处理),可以使用对齐方式实现:
from openpyxl import Workbook
from openpyxl.styles import Alignment
wb = Workbook()
ws = wb.active
# 不合并单元格,实现跨列居中效果
ws["A1"] = "跨列居中显示,不合并单元格"
# 设置跨列居中对齐
ws["A1"].alignment = Alignment(horizontal="centerContinuous", vertical="center")
# 设置A1到F1都使用这个对齐
for col in range(1, 7):
ws.cell(row=1, column=col).alignment = Alignment(horizontal="centerContinuous", vertical="center")
wb.save("跨列居中示例.xlsx")
print("✅ 跨列居中设置完成,没有合并单元格")
5.4 合并单元格后添加边框
合并单元格后边框可能会不完整,需要单独设置:
from openpyxl import Workbook
from openpyxl.styles import Border, Side
wb = Workbook()
ws = wb.active
# 合并单元格
ws.merge_cells("A1:C3")
ws["A1"] = "带边框的合并单元格"
# 设置边框
thin_border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
# 给合并区域的所有单元格设置边框
for row in range(1, 4):
for col in range(1, 4):
ws.cell(row=row, column=col).border = thin_border
wb.save("合并单元格加边框.xlsx")
六、使用pandas处理合并单元格
pandas默认会自动展开合并单元格,非常适合数据处理:
6.1 读取带合并单元格的Excel
import pandas as pd
# 读取带合并单元格的Excel,pandas会自动填充拆分后的单元格
df = pd.read_excel("合并单元格示例.xlsx", header=[0, 1]) # 多级表头
print("读取到的数据:")
print(df)
# 处理后保存,就没有合并单元格了
df.to_excel("拆分后的数据.xlsx", index=False)
print("✅ 已保存为无合并单元格的文件")
6.2 导出时设置合并单元格
pandas本身不支持合并单元格,但可以和openpyxl配合实现:
import pandas as pd
from openpyxl import load_workbook
# 准备数据
df = pd.DataFrame({
"日期": ["2026-01", "2026-01", "2026-02", "2026-02"],
"区域": ["华东", "华南", "华东", "华北"],
"销售额": [12000, 8500, 15000, 9800]
})
# 先保存为普通Excel
df.to_excel("pandas合并示例.xlsx", index=False)
# 用openpyxl打开合并单元格
wb = load_workbook("pandas合并示例.xlsx")
ws = wb.active
# 合并相同日期的单元格
ws.merge_cells("A2:A3")
ws.merge_cells("A4:A5")
# 设置居中
from openpyxl.styles import Alignment
ws["A2"].alignment = Alignment(horizontal="center", vertical="center")
ws["A4"].alignment = Alignment(horizontal="center", vertical="center")
wb.save("pandas合并示例.xlsx")
print("✅ pandas导出并合并单元格完成")
七、常见问题与解决方案
7.1 合并单元格后数据丢失
问题:合并单元格后,其他单元格的数据不见了
原因:Excel合并单元格只会保留左上角的内容,其他单元格内容会被丢弃
解决方案:
7.2 合并单元格后排序/筛选异常
问题:带合并单元格的表格排序、筛选时数据错乱
原因:合并单元格会导致行高列宽不一致,排序筛选时无法正确对应
解决方案:
7.3 拆分单元格后有空值
问题:拆分后只有第一个单元格有数据,其他都是空的
解决方案:
7.4 合并单元格的高度/宽度不对
问题:合并后的单元格内容显示不全
解决方案:
# 设置行高
ws.row_dimensions[1].height = 30
# 设置列宽
ws.column_dimensions["A"].width = 20
# 开启自动换行
from openpyxl.styles import Alignment
ws["A1"].alignment = Alignment(wrap_text=True)
7.5 批量合并速度慢
问题:上万行数据按条件合并时速度很慢
解决方案:
八、最佳实践
1. 数据处理与展示分离:数据处理阶段不要合并单元格,最后生成报表展示时再合并
2. 合并前备份:重要文件合并前先备份,避免数据丢失
3. 合并后验证:合并完成后检查内容是否正确,边框是否完整
4. 避免过度合并:不要为了美观过度合并单元格,会大幅增加后续维护难度
5. 批量处理先记录:批量合并/拆分前,先把所有需要处理的位置记录下来,再统一操作
6. 拆分必填充:拆分单元格后一定要填充所有空值,避免后续处理时出现空数据
7. 优先用跨列居中:只是需要居中效果的话,优先用centerContinuous对齐,不要真的合并单元格
合并单元格是把双刃剑,合理使用可以让表格更美观,滥用会给数据处理带来很多麻烦。建议在数据计算、分析、筛选阶段保持原始格式,最终输出报表时再根据需要合并。