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自动化的需求,欢迎在评论区留言交流,一起提升办公效率!

八、附录(补充资源)

相关推荐
笨鸟先飞,勤能补拙15 小时前
AI 赋能网络安全:技术全景、成熟度评估与实战案例
人工智能·python·安全·web安全·网络安全·sqlite·github
IT小白杨16 小时前
从环境制备到自动化工作流:多账号运营的工程化架构拆解
java·经验分享·自动化·安全架构·指纹浏览器
HiDev_16 小时前
【非标自动化】2、认识元器件(直线模组)
运维·自动化
长和信泰光伏储能16 小时前
京津冀光伏发电:绿色能源的未来之路
python·能源
浦信仿真大讲堂16 小时前
从重复操作到自动化闭环:如何让 CST 与 Python 真正协同起来
python·自动化·cst·仿真软件·达索软件
hPw0eKIqD16 小时前
使用二次封装的Excel COM 组件操作Excel\WPS ET中的区域、行和列
excel·wps
Gu Gu Study17 小时前
ScoutLoop开放域深度研究引擎(agent的初步设计想法)
人工智能·python
卷无止境17 小时前
写代码这件事,到底该讲究点什么?
后端·python
卷无止境18 小时前
循环复杂度到底在算什么,Python 代码怎么才能写得让人一看就懂
后端·python
lpfasd12318 小时前
MediaCrawler 项目深度分析
chrome·python·chrome devtools