Python 完美处理Excel合并单元格:拆分填充+自动合并

一、前言:Excel合并单元格的痛点

在日常办公、数据爬虫、数据分析、报表自动化场景中,Excel合并单元格是最让人头疼的格式问题之一。

常见痛点:

  • 合并单元格拆分后数据丢失:openpyxl原生拆分单元格后,只有左上角单元格保留数值,其余单元格为空,导致数据断层、无法用于筛选、透视、匹配数据。

  • 样式错乱:拆分/合并后字体、颜色、边框、对齐方式丢失,报表格式崩坏。

  • 无法可逆操作:拆分后想重新规整合并,没有通用自动化方案,只能手动操作。

  • 批量处理困难:多区域、多行多列合并单元格,人工修改效率极低。

为此,我基于 openpyxl 封装了一套无损、可逆、全覆盖的Excel合并单元格工具:

  1. 拆分合并单元格:自动填充全部空缺单元格,保留完整样式

  2. 智能合并单元格:按行/按列自动合并相同值、支持指定区域合并

  3. 全程保留:字体、填充色、边框、对齐、数字格式

  4. 拆分+合并双向联动,完美闭环

二、功能核心优势

  • 真·无损拆分:拆分后原合并区域所有单元格自动填充左上角原值,解决数据空白问题

  • 全样式复刻:逐单元格复制字体、背景填充、边框、对齐、数字格式

  • 三种合并模式:按列纵向合并、按行横向合并、自定义区域合并

  • 可逆闭环逻辑:拆分全填充 → 合并清冗余,和Excel原生逻辑一致

  • 安全不毁源文件:支持覆盖原文件/另存新文件双模式

三、完整工具源码(可直接运行)

依赖安装:

复制代码
pip install openpyxl

完整工具代码:

复制代码
from openpyxl import load_workbook
from copy import copy


# ===================== 拆分合并单元格(全区域填充值+保留样式) =====================
def unmerge_merged_cells_fill_all(file_path, sheet_name="Sheet1", output_path=None):
    """
    拆分所有合并单元格,拆分后原合并区域全部填充原左上角值,完整保留样式
    :param file_path: 输入Excel路径
    :param sheet_name: 工作表名称
    :param output_path: 输出路径,默认覆盖原文件
    :return: 处理后文件路径
    """
    # 加载工作簿,保留原格式
    wb = load_workbook(file_path)
    ws = wb[sheet_name]

    # 提前读取所有合并区域(拆分后会丢失合并信息,必须前置读取)
    merged_ranges = list(ws.merged_cells.ranges)
    if not merged_ranges:
        print("未检测到合并单元格,无需处理")
        return file_path

    # 遍历所有合并区域逐一处理
    for m_range in merged_ranges:
        min_row = m_range.min_row
        max_row = m_range.max_row
        min_col = m_range.min_col
        max_col = m_range.max_col

        # 获取左上角值与样式
        top_left_cell = ws.cell(row=min_row, column=min_col)
        top_left_value = top_left_cell.value

        # 拆分单元格
        ws.unmerge_cells(range_string=str(m_range))

        # 全区域填充值 + 复刻样式
        for row in range(min_row, max_row + 1):
            for col in range(min_col, max_col + 1):
                current_cell = ws.cell(row=row, column=col)
                current_cell.value = top_left_value
                # 完整复刻样式
                current_cell.font = copy(top_left_cell.font)
                current_cell.fill = copy(top_left_cell.fill)
                current_cell.border = copy(top_left_cell.border)
                current_cell.alignment = copy(top_left_cell.alignment)
                current_cell.number_format = top_left_cell.number_format

    # 保存文件
    output_path = output_path if output_path else file_path
    wb.save(output_path)
    print(f"✅ 拆分完成!共处理 {len(merged_ranges)} 个合并区域,文件保存至: {output_path}")
    return output_path


# ===================== 按列纵向自动合并相同值 =====================
def merge_cells_by_column(file_path, sheet_name="Sheet1", start_row=1, end_row=None, col=None, output_path=None):
    """
    单列纵向合并:同一列连续相同值自动合并
    """
    wb = load_workbook(file_path)
    ws = wb[sheet_name]
    max_row = ws.max_row if end_row is None else end_row

    merge_blocks = []
    current_start = start_row
    prev_val = ws.cell(row=start_row, column=col).value

    # 扫描连续相同值区块
    for r in range(start_row + 1, max_row + 1):
        curr_val = ws.cell(row=r, column=col).value
        if curr_val != prev_val:
            if current_start != r - 1:
                merge_blocks.append((current_start, col, r - 1, col))
            current_start = r
            prev_val = curr_val
    # 收尾最后一段
    if current_start != max_row:
        merge_blocks.append((current_start, col, max_row, col))

    # 执行合并+样式同步
    for min_r, min_c, max_r, max_c in merge_blocks:
        ws.merge_cells(min_row=min_r, min_col=min_c, max_row=max_r, max_col=max_c)
        top_cell = ws.cell(min_r, min_c)
        for r in range(min_r, max_r + 1):
            for c in range(min_c, max_c + 1):
                if not (r == min_r and c == min_c):
                    cell = ws.cell(r, c)
                    cell.value = None
                    cell.font = copy(top_cell.font)
                    cell.fill = copy(top_cell.fill)
                    cell.border = copy(top_cell.border)
                    cell.alignment = copy(top_cell.alignment)
                    cell.number_format = top_cell.number_format

    save_path = output_path if output_path else file_path
    wb.save(save_path)
    print(f"✅ 列合并完成,共 {len(merge_blocks)} 处合并,保存至:{save_path}")
    return save_path


# ===================== 按行横向自动合并相同值 =====================
def merge_cells_by_row(file_path, sheet_name="Sheet1", start_col=1, end_col=None, row=None, output_path=None):
    """
    单行横向合并:同一行连续相同值自动合并
    """
    wb = load_workbook(file_path)
    ws = wb[sheet_name]
    max_col = ws.max_column if end_col is None else end_col

    merge_blocks = []
    current_start = start_col
    prev_val = ws.cell(row=row, column=start_col).value

    for c in range(start_col + 1, max_col + 1):
        curr_val = ws.cell(row=row, column=c).value
        if curr_val != prev_val:
            if current_start != c - 1:
                merge_blocks.append((row, current_start, row, c - 1))
            current_start = c
            prev_val = curr_val
    if current_start != max_col:
        merge_blocks.append((row, current_start, row, max_col))

    for min_r, min_c, max_r, max_c in merge_blocks:
        ws.merge_cells(min_row=min_r, min_col=min_c, max_row=max_r, max_col=max_c)
        top_cell = ws.cell(min_r, min_c)
        for r in range(min_r, max_r + 1):
            for c in range(min_c, max_c + 1):
                if not (r == min_r and c == min_c):
                    cell = ws.cell(r, c)
                    cell.value = None
                    cell.font = copy(top_cell.font)
                    cell.fill = copy(top_cell.fill)
                    cell.border = copy(top_cell.border)
                    cell.alignment = copy(top_cell.alignment)
                    cell.number_format = top_cell.number_format

    save_path = output_path if output_path else file_path
    wb.save(save_path)
    print(f"✅ 行合并完成,共 {len(merge_blocks)} 处合并,保存至:{save_path}")
    return save_path


# ===================== 自定义区域合并 =====================
def manual_merge_range(file_path, sheet_name="Sheet1", min_r=1, min_c=1, max_r=1, max_c=1, output_path=None):
    """手动指定行列区域合并单元格"""
    wb = load_workbook(file_path)
    ws = wb[sheet_name]
    ws.merge_cells(min_row=min_r, min_col=min_c, max_row=max_r, max_col=max_c)
    top_cell = ws.cell(min_r, min_c)

    for r in range(min_r, max_r + 1):
        for c in range(min_c, max_c + 1):
            if not (r == min_r and c == min_c):
                cell = ws.cell(r, c)
                cell.value = None
                cell.font = copy(top_cell.font)
                cell.fill = copy(top_cell.fill)
                cell.border = copy(top_cell.border)
                cell.alignment = copy(top_cell.alignment)
                cell.number_format = top_cell.number_format

    save_path = output_path if output_path else file_path
    wb.save(save_path)
    print(f"✅ 区域合并完成!({min_r},{min_c})~({max_r},{max_c})")
    return save_path


# ===================== 一键联动:拆分填充 + 重新合并 =====================
if __name__ == "__main__":
    # 配置文件路径
    input_file = r"C:\Users\Administrator\Desktop\cs11.xlsx"
    temp_file = r"C:\Users\Administrator\Desktop\temp_拆分后.xlsx"
    final_file = r"C:\Users\Administrator\Desktop\最终规整表格.xlsx"

    # 1. 先拆分所有合并单元格,填充空白、保留样式
    unmerge_merged_cells_fill_all(input_file, output_path=temp_file)

    # 2. 按需重新合并(示例:对第1列纵向合并相同内容)
    merge_cells_by_column(temp_file, col=1, start_row=1, output_path=final_file)

    # 其他用法可单独调用
    # merge_cells_by_row(input_file, row=2)
    # manual_merge_range(input_file, min_r=2, min_c=1, max_r=5, max_c=3)

四、核心逻辑解析

1. 拆分单元格核心逻辑

很多人直接使用 ws.unmerge_cells() 会出现数据丢失,原因是:openpyxl 拆分后仅左上角单元格保留值

本工具解决方案:

  1. 先批量读取所有合并区域(必须拆分前读取,否则丢失记录)

  2. 记录每块区域左上角单元格的 值 + 全套样式

  3. 执行拆分操作

  4. 遍历区域内所有单元格,批量填充值、复刻样式

最终实现:拆分后数据完整、格式不变,可直接用于数据分析

2. 合并单元格核心逻辑

模拟人工规整表格逻辑:

  • 扫描行/列中连续相同的值,自动识别合并区块

  • 执行合并后,仅保留左上角值,其余单元格置空(符合Excel原生合并规则)

  • 同步复刻左上角样式,保证表格美观统一

五、常用调用示例

1. 仅拆分填充所有合并单元格

复制代码
unmerge_merged_cells_fill_all("test.xlsx", output_path="拆分后.xlsx")

2. 按列自动合并相同内容

复制代码
# 第2列,从第2行开始纵向合并 merge_cells_by_column("拆分后.xlsx", col=2, start_row=2)

3. 按行自动合并相同内容

复制代码
# 第3行横向合并 merge_cells_by_row("test.xlsx", row=3)

4. 手动指定区域合并

复制代码
# 2-5行,1-3列区域合并 manual_merge_range("test.xlsx", min_r=2, min_c=1, max_r=5, max_c=3)

六、适用场景总结

  • 数据清洗:解决合并单元格导致的pandas读取数据错位、空值问题

  • 报表自动化:自动规整Excel表格格式,替代人工合并拆分

  • 批量处理文件:海量Excel文件统一标准化格式

  • 数据可逆处理:拆分填充数据 → 规整合并,实现表格标准化闭环

相关推荐
汤姆小白5 小时前
08-应用部署
人工智能·python·机器学习·numpy·transformer
般若-波罗蜜6 小时前
MinerU高级用法,避坑指南(持续更新)
人工智能·python·语言模型·自然语言处理
用户8356290780516 小时前
使用 Python 对 PDF 文档进行数字签名的方法
后端·python
李可以量化7 小时前
PTrade 量化入门必学:get\_trading\_day 交易日函数全解与实战(下)
python
柒和远方7 小时前
LeetCode 139. 单词拆分 —— 从暴力回溯到 DP 完全背包
javascript·python·算法
半兽先生7 小时前
大模型实现金融文本分类
人工智能·python·金融
汤姆小白7 小时前
06-LoRA参数高效微调
人工智能·python·机器学习·transformer
铅笔侠_小龙虾8 小时前
Rust 学习(6)-所有权规则、移动语义、Clone 与 Copy
python·学习·rust
码云骑士8 小时前
65-AI-Agent基础原理-AutoGPT-四大架构全景解读
人工智能·python·架构