吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3232|回复: 14
收起左侧

[Python 原创] excel拆分工具分享

[复制链接]
pymoji 发表于 2025-6-9 11:08

工作中偶尔用到的工具分享

✅ 使用说明

  • 支持 .xlsx 文件格式
  • 可以按指定列拆分 Excel 表格
  • 保留原始表格的格式(字体、填充、边框、对齐方式等)
  • 修复所有与 None 相关的样式错误(如 Fill, Font, Style)使用copy完成深度复制
  • 附带简单GUI(TK实现)放置于同文件夹下
import openpyxl
from openpyxl.utils import get_column_letter
import os
import collections
from copy import copy

class splitExcel(object):

    def __init__(self, sourceFile, titleLine=None, splitColumn=None):
        self.sourceFile = sourceFile
        self.sourceWorkbook = openpyxl.load_workbook(sourceFile)
        self.targetWorkbook = openpyxl.Workbook()
        self.targetWorkbook.remove(self.targetWorkbook.active)

        # 源工作表(object对象)
        self.sourceWorksheet = None
        # 最大行数
        self.sourceWorkbookMaxRow = None
        # 最大列数
        self.sourceWorkbookMaxColumn = None
        # 源工作表索引号
        self.sourceWorksheetIndex = None
        # 标题所在行号,用户输入时索引从1开始,内部处理时请留意索引数
        self.titleLine = titleLine
        # 根据哪个列进行拆分,用户输入时索引从1开始,内部处理时请留意索引数
        self.splitColumn = splitColumn
        # 表头文字
        self.header = []
        # 各表数据
        self.data = collections.OrderedDict()
        # 样式信息
        self.formats = {}

    def readData(self):
        ws = self.sourceWorkbook.worksheets[self.sourceWorksheetIndex]
        for row in ws.iter_rows():
            values = [cell.value for cell in row]
            if row[0].row <= self.titleLine:
                self.header.append(values)
            else:
                v = values[self.splitColumn - 1]
                sheetName = self.clearSheetName(v)
                if sheetName not in self.data:
                    self.data[sheetName] = []
                self.data[sheetName].append(values)

    def selectSplitSheet(self):
        if len(self.sourceWorkbook.sheetnames) == 1:
            self.sourceWorksheet = self.sourceWorkbook.active
            self.sourceWorksheetIndex = 0
        else:
            print('在工作薄中找到以下工作表:')
            for i, name in enumerate(self.sourceWorkbook.sheetnames):
                print(i, name)
            n = int(input('请输入要拆分表的序号[0]: ').strip() or '0')
            self.sourceWorksheet = self.sourceWorkbook.worksheets[n]
            self.sourceWorksheetIndex = n

    def selectTitleLine(self):
        ws = self.sourceWorkbook.worksheets[self.sourceWorksheetIndex]
        print('打印所拆分工作表前10行,前5列数据:')
        max_col = min(5, ws.max_column)
        for x in range(10):
            row_values = [ws.cell(x + 1, y + 1).value for y in range(max_col)]
            print(f'第{x + 1}行:', row_values)
        titleLine = int(input('\n请输入标题行所在行号[2]:').strip() or '2')
        self.titleLine = titleLine

    def selectSplitColumn(self):
        ws = self.sourceWorkbook.worksheets[self.sourceWorksheetIndex]
        print(f'\n在工作表的标题行(第 {self.titleLine} 行)找到以下列:')
        for y in range(1, ws.max_column + 1):
            val = ws.cell(self.titleLine, y).value
            print(y, val)
        columnNum = int(input('请输入拆分列号[2]: ').strip() or '2')
        self.splitColumn = columnNum

    def readCellsStyle(self):
        ws = self.sourceWorkbook.worksheets[self.sourceWorksheetIndex]
        max_row = self.titleLine + 1
        max_col = ws.max_column

        styles = [[None] * (max_col + 11) for _ in range(max_row + 11)]
        fonts = [[None] * (max_col + 11) for _ in range(max_row + 11)]
        borders = [[None] * (max_col + 11) for _ in range(max_row + 11)]
        fills = [[None] * (max_col + 11) for _ in range(max_row + 11)]
        alignments = [[None] * (max_col + 11) for _ in range(max_row + 11)]
        number_formats = [[None] * (max_col + 11) for _ in range(max_row + 11)]
        protections = [[None] * (max_col + 11) for _ in range(max_row + 11)]
        heights = [None] * (max_row + 11)
        widths = [None] * (max_col + 11)

        for x in range(1, self.titleLine + 1):
            heights[x] = ws.row_dimensions[x].height
            for y in range(1, ws.max_column + 1):
                cell = ws.cell(x, y)
                styles[x][y] = copy(cell.style.replace('常规', 'Normal'))
                fonts[x][y] = copy(cell.font)
                borders[x][y] = copy(cell.border)
                fills[x][y] = copy(cell.fill)
                alignments[x][y] = copy(cell.alignment)
                number_formats[x][y] = copy(cell.number_format)
                protections[x][y] = copy(cell.protection)
                if widths[y] is None:
                    widths[y] = ws.column_dimensions[get_column_letter(y)].width

        self.formats['heights'] = heights
        self.formats['styles'] = styles
        self.formats['fonts'] = fonts
        self.formats['borders'] = borders
        self.formats['fills'] = fills
        self.formats['alignments'] = alignments
        self.formats['number_formats'] = number_formats
        self.formats['protections'] = protections
        self.formats['widths'] = widths

    def writeDataToNewWorkbook(self):
        for sheetName in self.data:
            ws = self.targetWorkbook.create_sheet(sheetName)
            for row in self.header:
                ws.append(row)
            for data_row in self.data[sheetName]:
                ws.append(data_row)

    def writeFormatToNewWorkbook(self):
        for sheetName in self.data:
            ws = self.targetWorkbook[sheetName]
            for x in range(1, ws.max_row + 1):
                xx = x if x <= self.titleLine else self.titleLine + 1
                height = self.formats['heights'][xx]
                if height:
                    ws.row_dimensions[x].height = height
                for y in range(1, ws.max_column + 1):
                    cell = ws.cell(x, y)
                    xx = x if x <= self.titleLine else self.titleLine + 1

                    # 设置 style
                    style = self.formats['styles'][xx][y]
                    if style is not None:
                        cell.style = style
                    else:
                        cell.style = "Normal"

                    # 设置 font
                    font = self.formats['fonts'][xx][y]
                    if font is not None:
                        cell.font = font

                    # 设置 border
                    border = self.formats['borders'][xx][y]
                    if border is not None:
                        cell.border = border

                    # 设置 fill
                    fill = self.formats['fills'][xx][y]
                    if fill is not None:
                        cell.fill = fill

                    # 设置 alignment
                    alignment = self.formats['alignments'][xx][y]
                    if alignment is not None:
                        cell.alignment = alignment

                    # 设置 number_format
                    number_format = self.formats['number_formats'][xx][y]
                    if number_format is not None:
                        cell.number_format = number_format

                    # 设置 protection
                    protection = self.formats['protections'][xx][y]
                    if protection is not None:
                        cell.protection = protection

                    # 设置列宽(只设置一次)
                    if y == 1 and x == 1:
                        ws.column_dimensions[get_column_letter(y)].width = self.formats['widths'][y]

    def clearSheetName(self, name, replaceAs='-'):
        invalidChars = r':\/?*[]:'
        for c in invalidChars:
            name = name.replace(c, replaceAs).strip()
        return name

    def make(self):
        self.selectSplitSheet()
        self.selectTitleLine()
        self.selectSplitColumn()
        print('开始读取数据...')
        self.readData()
        print('开始读取格式...')
        self.readCellsStyle()
        print('开始写入数据至分表...')
        self.writeDataToNewWorkbook()
        print('开始写入格式至分表...')
        self.writeFormatToNewWorkbook()

    def save(self, filename=None):
        if filename is None:
            path, ext = os.path.splitext(self.sourceFile)
            filename = f"{path}_分表{ext}"
        self.targetWorkbook.save(filename)
        self.sourceWorkbook.close()
        self.targetWorkbook.close()
        return filename

class saveWorksheetToWorkbook:

    def __init__(self, excelFile):
        self.excelFile = excelFile

    def saveTo(self, savePath=None, addNumToFilename=True):
        if savePath is None:
            path, ext = os.path.splitext(self.excelFile)
            savePath = path
            os.makedirs(savePath, exist_ok=True)

        wb = openpyxl.load_workbook(self.excelFile)
        sheetNames = wb.sheetnames
        wb.close()

        n = 0
        for sheetName in sheetNames:
            n += 1
            print('保存', n, sheetName)
            wb = openpyxl.load_workbook(self.excelFile)
            for ws in wb.worksheets:
                if ws.title != sheetName:
                    wb.remove(ws)
            xh = str(n) if addNumToFilename else ''
            filename = os.path.join(savePath, f"{xh}{sheetName}.xlsx")
            wb.save(filename)
            wb.close()
        return savePath

if __name__ == '__main__':
    file = r'C:\Users\Administrator\Desktop\2025Q1new7.xlsx'
    se = splitExcel(file)
    se.make()
    f = se.save()
    print('拆分汇总文件:', f)

    saveTo = saveWorksheetToWorkbook(f)
    p = saveTo.saveTo()
    print('拆分表保存文件夹:', p)

附带的GUI程序

请把上面的py文件保存为 splitExcel.py 这个名字,或者自己修改下面的代码

import tkinter as tk
from tkinter import filedialog, messagebox, simpledialog
from splitExcel import splitExcel, saveWorksheetToWorkbook  # 假设你的原脚本名为 splitExcel.py

class ExcelSplitterGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("Excel 拆分工具")
        self.root.geometry("500x400")
        self.root.resizable(False, False)

        # 初始化变量
        self.file_path = tk.StringVar()
        self.title_line = tk.IntVar(value=2)
        self.split_column = tk.IntVar(value=2)

        self.create_widgets()

    def create_widgets(self):
        # 文件选择框
        tk.Label(self.root, text="Excel 文件路径:").pack(pady=5)
        tk.Entry(self.root, textvariable=self.file_path, width=50).pack(pady=5)
        tk.Button(self.root, text="浏览", command=self.select_file).pack(pady=5)

        # 标题行输入
        tk.Label(self.root, text="标题行号(从1开始):").pack(pady=5)
        tk.Entry(self.root, textvariable=self.title_line, width=10).pack(pady=5)

        # 拆分列输入
        tk.Label(self.root, text="拆分列号(从1开始):").pack(pady=5)
        tk.Entry(self.root, textvariable=self.split_column, width=10).pack(pady=5)

        # 开始按钮
        tk.Button(self.root, text="开始拆分", command=self.run_split).pack(pady=20)

        # 日志输出区域
        self.log_text = tk.Text(self.root, height=10, width=60, state='disabled')
        self.log_text.pack(pady=10)

    def select_file(self):
        file_path = filedialog.askopenfilename(
            filetypes=[("Excel files", "*.xlsx *.xls")]
        )
        if file_path:
            self.file_path.set(file_path)

    def log(self, message):
        self.log_text.config(state='normal')
        self.log_text.insert(tk.END, message + "\n")
        self.log_text.config(state='disabled')
        self.log_text.see(tk.END)
        self.root.update_idletasks()

    def run_split(self):
        file_path = self.file_path.get()
        if not file_path:
            messagebox.showwarning("警告", "请选择一个 Excel 文件!")
            return

        title_line = self.title_line.get()
        split_column = self.split_column.get()

        try:
            self.log("开始初始化...")
            splitter = splitExcel(file_path, title_line, split_column)

            self.log("选择工作表...")
            splitter.selectSplitSheet()  # 可以根据需要优化为自动选第一个表

            self.log("读取数据...")
            splitter.readData()

            self.log("读取格式...")
            splitter.readCellsStyle()

            self.log("写入数据...")
            splitter.writeDataToNewWorkbook()

            self.log("写入格式...")
            splitter.writeFormatToNewWorkbook()

            self.log("保存文件...")
            output_file = splitter.save()

            self.log(f"拆分汇总文件已保存至:{output_file}")

            self.log("正在保存每个子表为独立文件...")
            saver = saveWorksheetToWorkbook(output_file)
            folder = saver.saveTo(savePath=None)
            self.log(f"拆分表保存文件夹:{folder}")

            messagebox.showinfo("完成", "拆分已完成!")

        except Exception as e:
            messagebox.showerror("错误", str(e))
            self.log(f"发生错误:{str(e)}")

if __name__ == "__main__":
    root = tk.Tk()
    app = ExcelSplitterGUI(root)
    root.mainloop()

原始文档

原始文档

工具打开

工具打开

拆分完成

拆分完成

结果验证

结果验证

免费评分

参与人数 1吾爱币 +7 热心值 +1 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

lujkhua 发表于 2026-6-17 06:44
nizsm123 发表于 2026-6-16 16:12
请在分享下,谢谢!

通过网盘分享的文件:Excel拆分工具.exe 链接: https://pan.baidu.com/s/19XNhb3V ... d=52pj 提取码: 52pj

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
nizsm123 + 1 + 1 谢谢@Thanks!

查看全部评分

lujkhua 发表于 2025-10-31 06:50
美化一下窗体图形界面:
[Python] 纯文本查看 复制代码
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from splitExcel import splitExcel, saveWorksheetToWorkbook  # 假设你的原脚本名为 splitExcel.py

class ExcelSplitterGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("Excel 拆分工具")
        self.root.geometry("550x500")
        self.root.resizable(False, False)
        
        # 设置主题样式
        self.style = ttk.Style()
        self.style.theme_use('clam')
        
        # 配置样式
        self.style.configure('Title.TLabel', font=('Arial', 12, 'bold'), foreground='#2c3e50')
        self.style.configure('Custom.TButton', font=('Arial', 10), background='#3498db', foreground='white')
        self.style.configure('Success.TButton', font=('Arial', 10, 'bold'), background='#27ae60', foreground='white')
        self.style.configure('TEntry', padding=5)
        
        # 初始化变量
        self.file_path = tk.StringVar()
        self.title_line = tk.IntVar(value=2)
        self.split_column = tk.IntVar(value=2)

        self.create_widgets()

    def create_widgets(self):
        # 主框架
        main_frame = ttk.Frame(self.root, padding="20")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 标题
        title_label = ttk.Label(main_frame, text="Excel 文件拆分工具", style='Title.TLabel')
        title_label.pack(pady=(0, 20))
        
        # 文件选择区域
        file_frame = ttk.LabelFrame(main_frame, text="文件选择", padding=10)
        file_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(file_frame, text="Excel 文件路径:").grid(row=0, column=0, sticky=tk.W, pady=5)
        file_entry = ttk.Entry(file_frame, textvariable=self.file_path, width=50)
        file_entry.grid(row=1, column=0, sticky=tk.W+tk.E, pady=5)
        
        browse_btn = ttk.Button(file_frame, text="浏览文件", command=self.select_file, style='Custom.TButton')
        browse_btn.grid(row=1, column=1, padx=(10, 0), pady=5)
        
        file_frame.columnconfigure(0, weight=1)
        
        # 参数设置区域
        params_frame = ttk.LabelFrame(main_frame, text="拆分参数", padding=10)
        params_frame.pack(fill=tk.X, pady=10)
        
        # 标题行设置
        ttk.Label(params_frame, text="标题行号(从1开始):").grid(row=0, column=0, sticky=tk.W, pady=5)
        title_spinbox = ttk.Spinbox(params_frame, from_=1, to=100, textvariable=self.title_line, width=10)
        title_spinbox.grid(row=0, column=1, sticky=tk.W, pady=5, padx=(10, 0))
        
        # 拆分列设置
        ttk.Label(params_frame, text="拆分列号(从1开始):").grid(row=1, column=0, sticky=tk.W, pady=5)
        split_spinbox = ttk.Spinbox(params_frame, from_=1, to=100, textvariable=self.split_column, width=10)
        split_spinbox.grid(row=1, column=1, sticky=tk.W, pady=5, padx=(10, 0))
        
        # 开始按钮
        start_btn = ttk.Button(main_frame, text="开始拆分", command=self.run_split, style='Success.TButton')
        start_btn.pack(pady=20)
        
        # 日志输出区域
        log_frame = ttk.LabelFrame(main_frame, text="执行日志", padding=10)
        log_frame.pack(fill=tk.BOTH, expand=True, pady=5)
        
        # 添加滚动条
        log_scrollbar = ttk.Scrollbar(log_frame)
        log_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.log_text = tk.Text(
            log_frame, 
            height=10, 
            width=60, 
            state='disabled',
            yscrollcommand=log_scrollbar.set,
            bg='#f8f9fa',
            fg='#2c3e50',
            font=('Consolas', 9),
            padx=10,
            pady=10
        )
        self.log_text.pack(fill=tk.BOTH, expand=True)
        
        log_scrollbar.config(command=self.log_text.yview)
        
        # 状态栏
        self.status_var = tk.StringVar(value="就绪")
        status_bar = ttk.Label(main_frame, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.pack(fill=tk.X, pady=(10, 0))

    def select_file(self):
        file_path = filedialog.askopenfilename(
            title="选择Excel文件",
            filetypes=[("Excel files", "*.xlsx *.xls"), ("All files", "*.*")]
        )
        if file_path:
            self.file_path.set(file_path)
            self.log(f"已选择文件: {file_path}")

    def log(self, message):
        self.log_text.config(state='normal')
        self.log_text.insert(tk.END, f"{message}\n")
        self.log_text.config(state='disabled')
        self.log_text.see(tk.END)
        self.root.update_idletasks()

    def run_split(self):
        file_path = self.file_path.get()
        if not file_path:
            messagebox.showwarning("警告", "请选择一个 Excel 文件!")
            return

        title_line = self.title_line.get()
        split_column = self.split_column.get()

        try:
            self.status_var.set("正在处理...")
            self.log("=" * 50)
            self.log("开始拆分Excel文件")
            self.log("=" * 50)
            
            self.log("开始初始化...")
            splitter = splitExcel(file_path, title_line, split_column)

            self.log("选择工作表...")
            splitter.selectSplitSheet()

            self.log("读取数据...")
            splitter.readData()

            self.log("读取格式...")
            splitter.readCellsStyle()

            self.log("写入数据...")
            splitter.writeDataToNewWorkbook()

            self.log("写入格式...")
            splitter.writeFormatToNewWorkbook()

            self.log("保存文件...")
            output_file = splitter.save()

            self.log(f"&#10003; 拆分汇总文件已保存至:{output_file}")

            self.log("正在保存每个子表为独立文件...")
            saver = saveWorksheetToWorkbook(output_file)
            folder = saver.saveTo(savePath=None)
            self.log(f"&#10003; 拆分表保存文件夹:{folder}")

            self.log("=" * 50)
            self.log("&#10003; 拆分任务已完成!")
            self.log("=" * 50)
            
            self.status_var.set("任务完成")
            messagebox.showinfo("完成", "拆分任务已完成!")

        except Exception as e:
            self.status_var.set("发生错误")
            messagebox.showerror("错误", str(e))
            self.log(f"&#10007; 发生错误:{str(e)}")
            self.log("=" * 50)
            self.log("&#10007; 任务失败")
            self.log("=" * 50)

if __name__ == "__main__":
    root = tk.Tk()
    app = ExcelSplitterGUI(root)
    root.mainloop()
robert1234 发表于 2025-6-11 08:51
这个很好用,对于想提取相同内容,避免了一次次筛选复制的繁杂和容易出错。
mytomsummer 发表于 2025-6-11 08:52
看一看学习一下.
yxf515321 发表于 2025-6-11 09:08
我在想,能不能支持录入不同的分割符号,然后对不同类型的数据进行分割
xuwupiaomiao 发表于 2025-6-16 10:40
可以加个多条件拆分 比如 按照主拆分列 B列 ,加个可选的副拆分列 C列,按照B列和C列拆分
Tuzki1 发表于 2025-6-16 14:33
有点厉害 赞一个
Goodrenran 发表于 2025-6-21 20:17
感谢大神
chm1988 发表于 2025-10-10 18:18
有人打包下吗
wanyihong 发表于 2025-10-11 13:15
非常好用多谢分享
浣犲ソ锛屾湭鏉? 发表于 2025-10-29 11:35
感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-7-26 10:16

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表