Python自动化复制Excel sheet表(openpyxl+win32com双方案,完美保留格式)

日常办公或数据处理中,我们经常会遇到需要复制Excel工作表(sheet)的场景------比如批量备份数据、合并多个Excel文件的sheet、重复生成格式一致的报表等。手动复制不仅耗时费力,还容易出现格式错乱、数据遗漏的问题,尤其面对多文件、多sheet的批量操作时,效率极低。

今天就给大家分享两种Python自动化复制Excel sheet表的实用方案,分别基于openpyxl和win32com实现,覆盖"轻量格式保留"和"完美格式保留"两种核心需求,同时对比两种方案的优劣性和适用场景,新手也能快速上手,彻底解放双手!

适用人群:Python新手、办公自动化从业者、需要批量处理Excel的职场人、数据分析师。

一、前置准备(环境+测试文件)

1.1 环境搭建

本次用到两个核心库,提前用pip安装即可,Python版本建议3.7及以上(兼容性更好),具体安装命令和库的作用如下:

python 复制代码
# 安装openpyxl(轻量,支持.xlsx格式,可手动实现格式复制)
pip install openpyxl

# 安装pywin32(用于调用win32com,依赖Windows系统和本地Excel)
pip install pywin32

补充说明:

  • openpyxl:仅支持.xlsx格式,无需依赖本地Excel,跨平台(Windows、Mac、Linux)可用,需手动编写代码实现格式复制。

  • win32com:依赖Windows系统+本地已安装的Excel(2016及以上版本最佳),支持.xls和.xlsx两种格式,可完美保留Excel的所有格式(包括宏、图表、条件格式等高级特性)。

1.2 测试文件准备

为了方便大家实操,提前准备两个测试文件,建议使用绝对路径(避免中文乱码和路径识别错误):

  • 源文件(source_file):包含1个或多个sheet,建议添加一些格式(字体、边框、合并单元格、数字格式等),测试格式复制效果,如下图,示例路径:r"E:\PycharmProjects\ExcelSheetCopy\业务状况表.xls"(可自行修改)。
  • 目标文件(target_file):用于接收复制的sheet,若文件不存在,代码会自动创建,示例路径:r"E:\PycharmProjects\ExcelSheetCopy\业务状况表-copy.xlsx"(可自行修改)。

注意:win32com支持.xls和.xlsx格式,openpyxl仅支持.xlsx格式,测试时可根据方案选择对应格式的源文件。

二、核心实现方案(附完整可运行代码)

以下两个方案均提供完整代码,可直接复制修改文件路径后运行,代码中包含详细注释,新手也能轻松看懂每一步的作用。

方案一:使用openpyxl(轻量跨平台,手动实现格式复制)

2.1 核心原理

通过openpyxl加载源Excel文件和目标Excel文件,手动复制sheet的列宽、行高、合并单元格、单元格值及样式(字体、边框、填充等),最终保存目标文件,实现"可控的格式保留"。适合不需要高级格式(如宏、图表),且需要跨平台使用的场景。

2.2 完整可运行代码(带详细注释)

python 复制代码
import openpyxl
from copy import copy
import os


def copy_sheet_with_format(source_file, target_file, sheet_name):
    """
    跨Excel文件复制工作表(手动实现格式保留,支持.xlsx格式)
    :param source_file: 源Excel文件绝对路径(必须存在,仅支持.xlsx)
    :param target_file: 目标Excel文件绝对路径(不存在则自动创建)
    :param sheet_name: 要复制的工作表名称(必须在源文件中存在)
    :return: 无返回值,成功后打印提示信息
    """
    # 1. 校验并加载源文件(判断文件是否存在、sheet是否存在)
    if not os.path.exists(source_file):
        # 抛出异常,提示文件不存在,便于排查问题
        raise FileNotFoundError(f"源文件 {source_file} 不存在!请检查文件路径是否正确。")
    # 加载源工作簿,data_only=False表示保留单元格格式(True仅保留数值)
    source_wb = openpyxl.load_workbook(source_file, data_only=False)
    # 判断指定sheet是否在源工作簿中
    if sheet_name not in source_wb.sheetnames:
        raise ValueError(f"源文件中没有名为 '{sheet_name}' 的工作表!请检查sheet名称是否正确。")
    # 获取源工作表对象
    source_sheet = source_wb[sheet_name]

    # 2. 加载或创建目标文件
    try:
        # 尝试加载目标文件(若文件已存在)
        target_wb = openpyxl.load_workbook(target_file)
    except FileNotFoundError:
        # 若目标文件不存在,新建工作簿
        target_wb = openpyxl.Workbook()
        # 删除新建工作簿默认的"Sheet"工作表(避免冗余)
        target_wb.remove(target_wb.active)

    # 3. 处理目标工作簿中的同名工作表(若存在则删除,避免冲突)
    if sheet_name in target_wb.sheetnames:
        target_wb.remove(target_wb[sheet_name])
    # 在目标工作簿中创建同名工作表,用于接收复制的内容
    target_sheet = target_wb.create_sheet(sheet_name)

    # 4. 复制源sheet的列宽(保证格式一致)
    for col in source_sheet.column_dimensions:
        target_sheet.column_dimensions[col].width = source_sheet.column_dimensions[col].width

    # 5. 复制源sheet的行高(保证格式一致)
    for row in source_sheet.row_dimensions:
        target_sheet.row_dimensions[row].height = source_sheet.row_dimensions[row].height

    # 6. 复制源sheet的合并单元格(避免合并格式丢失)
    for merged_range in source_sheet.merged_cells.ranges:
        # 将源sheet的合并单元格范围,复制到目标sheet
        target_sheet.merge_cells(str(merged_range))

    # 7. 复制每个单元格的值和样式(核心步骤,逐 cell 复制)
    for row in source_sheet.iter_rows():
        for cell in row:
            # 复制单元格的值
            new_cell = target_sheet.cell(row=cell.row, column=cell.column, value=cell.value)
            # 复制单元格样式(若有)
            if cell.has_style:
                # 使用copy()避免样式对象跨工作簿引用报错
                new_cell.font = copy(cell.font)  # 字体
                new_cell.border = copy(cell.border)  # 边框
                new_cell.fill = copy(cell.fill)  # 填充色
                new_cell.number_format = cell.number_format  # 数字格式(如日期、百分比)
                new_cell.protection = copy(cell.protection)  # 保护设置
                new_cell.alignment = copy(cell.alignment)  # 对齐方式

    # 8. 保存目标文件(完成复制)
    target_wb.save(target_file)
    print(f"✅ 成功将工作表 '{sheet_name}' 从 '{source_file}' 复制到 '{target_file}',格式已保留。")

# 【核心调用代码】(修改以下路径和sheet名称即可运行)
if __name__ == "__main__":
    # 源文件路径(仅支持.xlsx格式,绝对路径)
    SOURCE_EXCEL = r"E:\PycharmProjects\ExcelSheetCopy\业务状况表.xlsx"
    # 目标文件路径(绝对路径,不存在会自动创建)
    TARGET_EXCEL = r"E:\PycharmProjects\ExcelSheetCopy\业务状况表-copy.xlsx"
    # 要复制的工作表名称(必须与源文件中的sheet名称一致)
    SHEET_NAME = "业务状况表(日报)"

    # 调用函数,执行复制操作
    copy_sheet_with_format(SOURCE_EXCEL, TARGET_EXCEL, SHEET_NAME)

运行结果如下:

2.3 关键注意点

  • 格式支持:可保留字体、边框、填充、合并单元格、数字格式等基础格式,但不支持宏、图表、条件格式等高级特性。

  • 文件格式:仅支持.xlsx格式,若源文件是.xls格式,需先转换为.xlsx,或使用win32com方案。

  • 路径问题:必须使用绝对路径,避免中文路径(若需使用中文,确保Python环境编码为utf-8)。

  • 性能:适合中小型Excel文件(10万行以内),大型文件可能会出现卡顿(可优化为分块读取)。

方案二:使用win32com(Windows专属,完美保留所有格式)

2.1 核心原理

通过win32com调用Windows系统中的Excel应用程序(需本地安装Excel),模拟人工操作Excel的"复制-粘贴"流程,可完美保留Excel的所有格式,包括宏、图表、条件格式、数据验证等高级特性,适合对格式要求极高的场景。

2.2 完整可运行代码(带详细注释)

python 复制代码
import os
import win32com.client as win32

def copy_sheet_via_excel(source_file, target_file, sheet_name, overwrite=True):
    """
    通过Excel应用程序复制工作表(完美保留全部格式,支持.xls和.xlsx)
    :param source_file: 源Excel文件绝对路径(必须存在)
    :param target_file: 目标Excel文件绝对路径(不存在则自动创建)
    :param sheet_name: 要复制的工作表名称(必须在源文件中存在)
    :param overwrite: 目标工作簿存在同名sheet时,是否覆盖(True=覆盖,False=报错)
    :return: (bool, str) 第一个元素表示是否成功,第二个元素为错误信息(成功时为None)
    """
    # 1. 参数校验(提前排查基础错误)
    if not os.path.isfile(source_file):
        error_info = f"❌ 错误:源文件不存在 - {source_file},请检查路径是否正确。"
        return False, error_info
    if not sheet_name:
        error_info = "❌ 错误:工作表名称不能为空!"
        return False, error_info

    # 初始化Excel应用程序和工作簿对象(避免资源泄露,用finally释放)
    excel = None
    source_wb = None
    target_wb = None
    try:
        # 2. 启动Excel应用程序(Visible=False表示不显示Excel窗口,后台运行)
        excel = win32.gencache.EnsureDispatch('Excel.Application')
        excel.Visible = False
        excel.DisplayAlerts = False  # 禁止弹出提示框(如覆盖确认、格式兼容提示)

        # 3. 打开源工作簿(只读模式,避免修改源文件)
        print(f"📂 正在打开源文件:{source_file}")
        source_wb = excel.Workbooks.Open(source_file, ReadOnly=True)
        # 检查源工作簿中是否存在指定工作表
        try:
            source_sheet = source_wb.Worksheets(sheet_name)
        except Exception as e:
            error_info = f"❌ 错误:源工作簿中不存在名为 '{sheet_name}' 的工作表 - {str(e)}"
            return False, error_info

        # 4. 打开或创建目标工作簿
        print(f"📂 正在打开/创建目标文件:{target_file}")
        if os.path.isfile(target_file):
            # 目标文件已存在,直接打开
            target_wb = excel.Workbooks.Open(target_file)
        else:
            # 目标文件不存在,新建工作簿并保存
            target_wb = excel.Workbooks.Add()
            target_wb.SaveAs(target_file)
            print(f"✅ 已创建新工作簿:{target_file}")

        # 5. 处理目标工作簿中的同名工作表(避免冲突)
        sheet_names = [ws.Name for ws in target_wb.Worksheets]
        if sheet_name in sheet_names:
            if overwrite:
                # 允许覆盖,删除目标工作簿中的同名sheet
                print(f"⚠️  目标工作簿中已存在同名工作表 '{sheet_name}',正在删除...")
                target_wb.Worksheets(sheet_name).Delete()
                print(f"✅ 已删除原工作表 '{sheet_name}'")
            else:
                # 不允许覆盖,直接报错退出
                error_info = f"❌ 错误:目标工作簿中已存在同名工作表 '{sheet_name}',且未设置覆盖模式。"
                return False, error_info

        # 6. 复制工作表到目标工作簿(核心步骤,完美保留格式)
        # After参数:指定复制到目标工作簿的最后一个工作表之后
        after_sheet = target_wb.Worksheets(target_wb.Worksheets.Count)
        source_sheet.Copy(After=after_sheet)
        print(f"✅ 已复制工作表 '{sheet_name}' 到目标工作簿")

        # 7. 保存目标工作簿,完成复制
        target_wb.Save()
        print(f"✅ 目标工作簿已保存:{target_file}")

        # 8. 关闭源工作簿(不保存修改,避免误改源文件)
        source_wb.Close(SaveChanges=False)
        print("✅ 源工作簿已关闭")

        # 复制成功,返回结果
        success_info = f"🎉 成功将工作表 '{sheet_name}' 从 '{source_file}' 复制到 '{target_file}',格式已完美保留。"
        return True, success_info

    except Exception as e:
        # 捕获复制过程中的所有异常,返回错误信息
        error_info = f"❌ 复制过程中发生错误:{str(e)}"
        return False, error_info

    finally:
        # 确保释放资源(无论是否发生异常,都关闭工作簿和Excel应用)
        if source_wb:
            try:
                source_wb.Close(SaveChanges=False)
            except:
                pass
        if target_wb:
            try:
                target_wb.Close(SaveChanges=False)  # 已保存,无需再次保存
            except:
                pass
        if excel:
            try:
                excel.Quit()  # 关闭Excel应用程序
            except:
                pass
            del excel  # 释放COM对象引用,避免内存泄露


# 【核心调用代码】(修改以下路径和sheet名称即可运行)
if __name__ == "__main__":
    # 源文件路径(支持.xls和.xlsx格式,绝对路径)
    SOURCE_EXCEL = r"E:\PycharmProjects\ExcelSheetCopy\业务状况表.xls"
    # 目标文件路径(绝对路径,不存在会自动创建)
    TARGET_EXCEL = r"E:\PycharmProjects\ExcelSheetCopy\业务状况表-copy.xlsx"
    # 要复制的工作表名称(必须与源文件中的sheet名称一致)
    SHEET_NAME = "业务状况表(日报)"

    # 调用函数,执行复制操作,允许覆盖同名sheet
    res = copy_sheet_via_excel(SOURCE_EXCEL, TARGET_EXCEL, SHEET_NAME, overwrite=True)
    # 打印执行结果
    print(res[1])

运行结果如下:

2.3 关键注意点

  • 环境依赖:仅支持Windows系统,且本地必须安装Excel(2016及以上版本最佳),Mac/Linux系统无法使用。

  • 格式支持:完美保留所有格式,包括宏、图表、条件格式、数据验证、公式等,是格式要求极高场景的首选。

  • 资源释放:必须在finally中关闭工作簿和Excel应用程序,否则会导致Excel进程后台残留(可在任务管理器中结束)。

  • 权限问题:若报错"权限不足",需关闭目标文件,或用管理员身份运行Python脚本。

三、两种方案优劣性+使用场景对比(重点)

很多同学会纠结"该选哪种方案",这里整理了详细的对比表格,结合自身需求选择即可,一目了然:

对比维度 openpyxl方案 win32com方案
格式保留 支持基础格式(字体、边框、合并单元格等),不支持宏、图表等高级特性 完美保留所有格式(包括宏、图表、条件格式等高级特性)
文件格式支持 仅支持.xlsx格式 支持.xls和.xlsx两种格式
运行环境 跨平台(Windows、Mac、Linux),无需安装Excel 仅支持Windows,必须安装Excel
操作难度 中等(需手动编写格式复制代码) 简单(调用Excel接口,无需手动处理格式)
性能表现 中小型文件(10万行以内)流畅,大型文件易卡顿 适配各类文件大小,性能更稳定(依赖Excel自身处理能力)
资源占用 轻量,占用内存少 较重,需启动Excel进程,占用内存较多
适用场景 1. 跨平台使用;2. 仅需保留基础格式;3. 中小型Excel文件;4. 无本地Excel环境 1. Windows系统;2. 需完美保留所有格式(含宏、图表);3. 大型Excel文件;4. 对格式要求极高的办公场景

总结建议:若你是Windows系统、有Excel环境、对格式要求高,优先选win32com;若需要跨平台、仅需基础格式、无Excel环境,选openpyxl即可。

四、常见应用场景(贴合实际,直接复用)

结合两种方案的特点,整理4个最常见的应用场景,可直接修改代码复用:

场景1:同一工作簿内复制sheet并重命名

需求:将"业务状况表(日报)"复制一份,重命名为"业务状况表(周报)",用于后续编辑。

实现思路:无需额外处理,只需将目标文件路径改为源文件路径(同一工作簿),复制后修改sheet名称即可(openpyxl和win32com均可实现)。

场景2:不同工作簿间批量复制多个sheet

需求:将源文件中的"日报""周报""月报"3个sheet,批量复制到目标文件中。

实现思路:循环调用上述两个方案的函数,遍历需要复制的sheet名称列表,批量执行复制操作。

python 复制代码
# 示例(以win32com为例,openpyxl同理)
if __name__ == "__main__":
    SOURCE_EXCEL = r"E:\PycharmProjects\ExcelSheetCopy\业务状况表.xls"
    TARGET_EXCEL = r"E:\PycharmProjects\ExcelSheetCopy\业务状况表-copy.xlsx"
    # 需要批量复制的sheet名称列表
    SHEET_NAMES = ["业务状况表(日报)", "业务状况表(周报)", "业务状况表(月报)"]
    
    for sheet in SHEET_NAMES:
        res = copy_sheet_via_excel(SOURCE_EXCEL, TARGET_EXCEL, sheet, overwrite=True)
        print(f"sheet '{sheet}':{res[1]}")

场景3:复制sheet时筛选特定数据

需求:复制"业务状况表(日报)",但仅保留"销售额>10000"的行数据,同时保留原有格式。

实现思路:在openpyxl方案中,复制单元格时添加条件判断,仅复制符合条件的单元格;win32com方案可先复制整个sheet,再通过Excel接口筛选数据。

场景4:定时自动复制(每日/每周备份)

需求:每天固定时间(如18:00)自动复制当天的业务报表sheet,实现自动备份。

实现思路:结合schedule库,定时调用复制函数,示例代码(需安装schedule:pip install schedule):

python 复制代码
import schedule
import time

# 定义定时任务
def auto_copy_sheet():
    SOURCE_EXCEL = r"E:\PycharmProjects\ExcelSheetCopy\业务状况表.xls"
    TARGET_EXCEL = r"E:\PycharmProjects\ExcelSheetCopy\备份\业务状况表-{}.xlsx".format(time.strftime("%Y%m%d"))
    SHEET_NAME = "业务状况表(日报)"
    res = copy_sheet_via_excel(SOURCE_EXCEL, TARGET_EXCEL, SHEET_NAME, overwrite=True)
    print(f"定时备份结果:{res[1]}")

# 每天18:00执行备份任务
schedule.every().day.at("18:00").do(auto_copy_sheet)

# 持续运行定时任务
while True:
    schedule.run_pending()
    time.sleep(60)  # 每60秒检查一次任务

五、常见问题与解决方案(避坑指南)

实操过程中可能会遇到一些问题,整理了最常见的5个问题及解决方案,帮大家快速避坑:

问题1:运行代码报错"文件不存在"

原因:文件路径错误(相对路径识别失败)、文件名拼写错误、文件被占用。

解决方案:1. 统一使用绝对路径(复制文件路径后,在前面加r,如r"E:\test.xlsx");2. 检查文件名和sheet名称是否一致;3. 关闭打开的Excel文件,释放占用。

问题2:复制后格式错乱/数据丢失

原因:openpyxl方案未手动复制某类格式(如合并单元格、数字格式);win32com方案未关闭Excel提示框。

解决方案:1. openpyxl方案检查代码中是否包含列宽、行高、合并单元格、样式的复制代码;2. win32com方案确保添加excel.DisplayAlerts = False

问题3:win32com报错"无法创建Excel应用程序"

原因:本地未安装Excel、Excel版本不兼容、权限不足。

解决方案:1. 安装Excel 2016及以上版本;2. 用管理员身份运行Python脚本;3. 重新安装pywin32(pip uninstall pywin32 && pip install pywin32)。

问题4:中文路径/中文sheet名称报错

原因:Python环境编码不支持中文,或路径中包含特殊字符。

解决方案:1. 路径前面加r(如r"E:\测试文件.xlsx");2. 确保Python环境编码为utf-8(可在脚本开头添加# -*- coding: utf-8 -*-)。

问题5:Excel进程后台残留(关闭Python后,Excel仍在任务管理器中)

原因:win32com方案未正确释放资源(未关闭工作簿或Excel应用)。

解决方案:1. 确保代码中finally块正确关闭source_wb、target_wb和excel;2. 手动在任务管理器中结束"EXCEL.EXE"进程。

六、进阶优化(可复用性提升)

为了让代码更具复用性,适合实际工作场景,可对上述代码进行以下优化:

优化1:封装为可复用函数(已实现)

两种方案均已封装为函数,可直接导入其他脚本调用,只需传入源文件路径、目标文件路径、sheet名称即可。

优化2:添加日志记录(便于排查问题)

使用logging库,记录复制过程、成功/失败信息,避免每次运行都手动查看打印结果,示例:

python 复制代码
import logging

# 配置日志(保存到文件,同时打印到控制台)
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    handlers=[logging.FileHandler("excel_copy_log.log"), logging.StreamHandler()]
)

# 在函数中使用日志替代print
logging.info(f"正在打开源文件:{source_file}")
logging.error(error_info)

优化3:批量处理多文件夹Excel文件

遍历指定文件夹下的所有Excel文件,自动复制每个文件中的指定sheet到目标文件,适合批量合并场景(可结合os.walk()实现)。

优化4:添加异常重试机制

对于网络文件、易被占用的文件,添加重试机制,避免因临时错误导致复制失败:

python 复制代码
from retry import retry  # 需安装:pip install retry

# 重试机制:失败后重试3次,每次间隔2秒
@retry(tries=3, delay=2)
def copy_sheet_via_excel(source_file, target_file, sheet_name, overwrite=True):
    # 原函数代码不变
    pass

七、总结与展望

本文分享了Python自动化复制Excel sheet表的两种核心方案,分别解决"轻量跨平台"和"完美格式保留"的需求:

  • openpyxl:跨平台、轻量,适合基础格式保留,无需Excel环境,新手易上手。

  • win32com:Windows专属,完美保留所有格式,适合对格式要求极高的办公场景。

通过两种方案的对比和实操代码,相信大家能快速根据自身需求选择合适的方法,彻底摆脱手动复制的繁琐工作。

后续拓展方向:结合Python的数据清洗、可视化功能,实现Excel全流程自动化(如复制sheet后自动统计数据、生成图表);也可以封装为桌面工具,方便非技术人员使用。

如果大家在实操过程中遇到问题,或者有其他Excel自动化的需求,欢迎在评论区留言交流,一起提升办公效率!

八、附录(补充资源)

相关推荐
勤劳的进取家2 小时前
Excel 公式使用手册(精简)
算法·excel
天空属于哈夫克32 小时前
告别重复粘贴:如何利用 API 实现企业微信群公告自动更新
数据库·自动化·企业微信·rpa
迷藏4942 小时前
**基于Python与Neo4j的知识图谱构建实践:从数据到语义网络的跃迁**在人工智能与大数据深度融合
java·人工智能·python·neo4j
kcuwu.2 小时前
Anaconda创建虚拟环境及Pycharm关联(搭建ai智能体准备工作)
ide·python·pycharm
小陈工4 小时前
Python安全编程实践:常见漏洞与防护措施
运维·开发语言·人工智能·python·安全·django·开源
腾讯蓝鲸智云10 小时前
嘉为蓝鲸可观测系列产品入选Gartner《中国智能IT监控与日志分析工具市场指南》
运维·人工智能·信息可视化·自动化
2401_8747325310 小时前
为你的Python脚本添加图形界面(GUI)
jvm·数据库·python
FreakStudio11 小时前
0 元学嵌入式 GUI!保姆级 LVGL+MicroPython 教程开更,从理论到实战全搞定
python·单片机·嵌入式·面向对象·电子diy
arvin_xiaoting11 小时前
OpenClaw 2026.3.23 重磅更新:UI焕新+安全加固+生态爆发,AI助手进入新纪元
自动化·llm·claude·工作流·ai agent·飞书机器人·openclaw