使用openpyxl出现单元格边框丢失后的,解决方法

本文提供了解决使用openpyxl处理Excel文件时,合并单元格边框在保存后丢失的问题。通过修改openpyxl源代码中特定行,可以避免此问题。适用于openpyxl版本2.5.8。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

亲身测试过有效!

实现功能:

使用openpyxl读取excel中的格式,然后进行数据的填充,最后另存为一个文件;

详细参考:https://bitbucket.org/openpyxl/openpyxl/issues/700/cell-borders-lost-on-save

问题:

    保存后合并单元格的地方出现边框的丢失;

     openpyxl版本:2.5.8

解决方案:

    https://bitbucket.org/openpyxl/openpyxl/pull-requests/272/removed-unnecessary-cleaning-of-merged/diff

修改  openpyxl/reader/worksheet.py,注释掉255, 256行,根据你的情况选择,可能是253,254

 

各位大佬,如果这篇转载的文章有用!请打赏一下下面的原作者,1块两块的喝个水

 


作者:lcg102
链接:https://www.jianshu.com/p/976fbd75f3ee
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

<think>我们有一个任务:使用openpyxl将一个工作簿中的工作表复制到另一个工作簿。 根据引用[2]中,我们看到有`workbook.copy_worksheet(sheet)`方法,但这是在同一个工作簿内复制工作表。 而我们需要将工作表复制到另一个工作簿,所以不能直接使用这个方法,因为复制后的工作表仍然在同一个工作簿中。 思路: 1. 打开源工作簿(包含要复制的工作表)和目标工作簿(要将工作表复制到的工作簿)。 2. 在源工作簿中复制工作表(这会创建一个副本,但仍在源工作簿中)。 3. 然后,我们需要将这个副本移动到目标工作簿。但是,openpyxl并没有直接提供将工作表从一个工作簿移动到另一个工作簿的方法。 4. 因此,我们可以考虑创建一个新的工作表在目标工作簿中,然后将源工作表中的所有内容(包括数据和样式)复制到新工作表中。 另一种方法使用openpyxl的`copy_worksheet`函数,但注意它只能在同一工作簿内复制。所以我们需要将复制的临时工作表从源工作簿移动到目标工作簿,但openpyxl的工作表对象是与工作簿绑定的,不能直接移动。 因此,我们可以手动复制数据,包括单元格的值、样式、合并的单元格等。 但是,openpyxl提供了复制单元格的功能,我们可以遍历源工作表的每一个单元格,将其值、样式等复制到目标工作表的对应单元格。 然而,openpyxl 2.6版本及以上提供了一个方法:`copy_worksheet`只能在同一个工作簿内复制,但我们可以先复制到源工作簿,然后将其添加到目标工作簿,然后再从源工作簿中删除?但这样会修改源工作簿,而且如果不想修改源工作簿,则不可行。 实际上,我们可以这样: 1. 在源工作簿中复制工作表,得到一个新工作表(副本)。 2. 将这个副本工作表从源工作簿中移除(使用`workbook.remove(sheet)`)然后添加到目标工作簿?但是,openpyxl不允许直接移动工作表到另一个工作簿。 所以,我们只能手动复制所有内容。 但是,有一个更简单的方法使用`openpyxl.worksheet.copier.WorksheetCopy`类(虽然这个类在文档中没有详细说明,但我们可以查看源码或使用它)。不过,为了稳定性,我们可以采用另一种方式。 参考引用[4]中的基本操作,我们可以创建一个新工作表,然后复制源工作表的所有行。 然而,复制不仅仅是值,还包括样式、公式、合并单元格等。 在openpyxl中,复制整个工作表到另一个工作簿的步骤: 方法1(推荐,使用openpyxl的copy_worksheet,但需要处理不同工作簿): 由于openpyxl的copy_worksheet只能在同一工作簿内复制,我们可以先将要复制的工作表复制到源工作簿的一个临时副本,然后将这个临时副本工作表添加到目标工作簿,最后删除源工作簿中的临时副本(如果我们不想改变源工作簿,则需要在内存中操作,且最后保存时注意不要保存源工作簿,或者我们使用临时文件?)。这可能会影响源工作簿。 如果我们不想修改源工作簿,那么我们可以采用以下步骤: 步骤: 1. 加载源工作簿和目标工作簿。 2. 在源工作簿中复制要复制的工作表(这会生成一个副本,比如名字为“SheetName Copy”)。 3. 将这个副本工作表从源工作簿中取出来,然后添加到目标工作簿中(使用目标工作簿的`create_sheet`方法,并将这个副本作为参数?但是openpyxl不允许直接添加另一个工作簿的工作表)。 4. 然后,从源工作簿中删除这个副本(如果我们不打算保存源工作簿,那么这一步在内存中操作,且不保存源工作簿,则不会影响源文件)。 但是,openpyxl的Worksheet对象是与Workbook绑定的,不能直接添加到另一个Workbook。所以我们需要创建一个新的工作表在目标工作簿中,然后复制源工作表的所有属性和数据。 方法2:手动复制 我们可以遍历源工作表的每一个单元格,将值、样式、公式等复制到目标工作表的对应单元格。同时,还要复制合并单元格、行高、列宽等设置。 这是一个比较繁琐的过程,但我们可以编写一个函数来实现。 幸运的是,openpyxl提供了复制单元格样式的功能,我们可以使用`copy`方法。 但是,有一个更简单的方法使用openpyxl的`WorksheetCopy`类(在openpyxl.worksheet.copier中),但这是内部API,可能会变化。 考虑到稳定性,我们可以自己写一个复制函数,或者参考社区的做法。 实际上,在openpyxl的官方文档中,没有提供直接跨工作簿复制工作表的方法。因此,我们需要手动复制。 手动复制的主要步骤: 1. 在目标工作簿中创建一个新工作表,命名为源工作表的名称(或指定的名称)。 2. 复制源工作表的行高和列宽。 3. 复制合并单元格的区域。 4. 遍历源工作表的每个单元格,复制单元格的值和样式。 但是,样式包括字体、边框、填充、对齐、保护等,复制起来比较麻烦。 在openpyxl中,我们可以使用`copy`模块中的`copy`函数来复制单元格的样式。 示例代码: 首先,导入: ```python from openpyxl import load_workbook from openpyxl.styles import Protection from copy import copy ``` 然后,定义一个复制工作表的函数: 由于这个过程比较复杂,我们可以参考一些已有的实现。例如,在openpyxl的issue中有人提供了这样的函数。 这里,我参考一个常用的方法: ```python def copy_sheet(source_sheet, target_sheet): # 复制合并的单元格 for merged_cell in source_sheet.merged_cells.ranges: target_sheet.merge_cells(str(merged_cell)) # 复制行高和列宽 for row in source_sheet.row_dimensions: target_sheet.row_dimensions[row] = source_sheet.row_dimensions[row] for col in source_sheet.column_dimensions: target_sheet.column_dimensions[col] = source_sheet.column_dimensions[col] # 复制每个单元格 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: 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) # 复制其他属性,如打印设置等(可选) target_sheet.sheet_properties = source_sheet.sheet_properties target_sheet.page_setup = source_sheet.page_setup target_sheet.page_margins = source_sheet.page_margins target_sheet.print_options = source_sheet.print_options # 注意:还有一些属性可能没有复制,根据需求添加 ``` 然后,我们使用这个函数来复制工作表到另一个工作簿: 步骤: 1. 打开源工作簿和目标工作簿。 2. 在目标工作簿中创建一个新的工作表(可以指定名字,避免重复)。 3. 使用上述函数复制源工作表中的内容到目标工作簿的新工作表中。 注意:如果源工作簿中有图表、图片等,这种方法可能无法复制。因为openpyxl对图表的支持有限,复制图表需要额外的处理。如果工作表包含图表,可能需要使用其他方法。 因此,如果只需要复制数据和样式,上述方法可行。 另外,也可以考虑使用临时文件的方法:先复制整个工作簿,然后删除不需要的工作表,只保留需要的工作表。但这样效率低,尤其是当工作簿很大时。 根据需求,我们选择手动复制的方法。 具体代码实现: 假设我们有两个工作簿文件:`source.xlsx`和`target.xlsx`,我们要将`source.xlsx`中的工作表`Sheet1`复制到`target.xlsx`中,新工作表命名为`Copied Sheet`。 ```python from openpyxl import load_workbook from copy import copy # 定义复制工作表的函数 def copy_sheet(source_sheet, target_workbook, sheet_name=None): if sheet_name is None: sheet_name = source_sheet.title # 在目标工作簿中创建新工作表 new_sheet = target_workbook.create_sheet(sheet_name) # 复制合并单元格 for merged_cell in source_sheet.merged_cells.ranges: new_sheet.merge_cells(str(merged_cell)) # 复制行高 for r_idx, row in enumerate(source_sheet.row_dimensions): # row_dimensions是一个字典,键是行号(整数) if row in source_sheet.row_dimensions: new_sheet.row_dimensions[row] = copy(source_sheet.row_dimensions[row]) # 复制列宽 for c_idx, col in enumerate(source_sheet.column_dimensions): # column_dimensions是一个字典,键是列字母(如'A') if col in source_sheet.column_dimensions: new_sheet.column_dimensions[col] = copy(source_sheet.column_dimensions[col]) # 复制每个单元格 for row in source_sheet.iter_rows(): for cell in row: new_cell = new_sheet.cell(row=cell.row, column=cell.column, value=cell.value) if cell.has_style: 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) if cell.protection else Protection() new_cell.alignment = copy(cell.alignment) # 复制其他属性 new_sheet.sheet_properties = source_sheet.sheet_properties new_sheet.page_setup = source_sheet.page_setup new_sheet.page_margins = source_sheet.page_margins new_sheet.print_options = source_sheet.print_options return new_sheet # 加载源工作簿 source_workbook = load_workbook('source.xlsx') source_sheet = source_workbook['Sheet1'] # 假设要复制的工作表名为'Sheet1' # 加载目标工作簿 target_workbook = load_workbook('target.xlsx') # 复制工作表 copy_sheet(source_sheet, target_workbook, sheet_name='Copied Sheet') # 保存目标工作簿 target_workbook.save('target.xlsx') ``` 注意:上述代码中,复制行高和列宽时,我们使用了`copy`函数来复制维度对象。另外,复制单元格时,我们复制了字体、边框、填充、数字格式、保护和对齐等样式。 但是,请注意,如果工作表中有公式,那么复制后公式中的引用可能会改变(特别是当引用其他工作表时),这需要额外处理。如果源工作表中的公式是引用同一工作表内的单元格,那么复制后公式会保持不变(因为单元格位置相同)。但如果是引用其他工作表,那么公式中的工作表引用可能还是指向源工作簿中的工作表,这会导致错误。因此,复制公式时需要谨慎。 如果只需要值,可以在复制后,将公式转换为值。但上述代码中,我们直接复制了值,实际上,如果单元格是公式,我们复制的是公式的结果。但是,在openpyxl中,当我们读取一个包含公式的单元格时,`cell.value`返回的是公式计算的结果(如果文件是上次保存后未重新计算,可能是缓存值)。如果我们想保留公式,应该使用`cell.data_type`来判断,如果是公式,则复制公式字符串(`cell.value`可能返回的是公式字符串,但openpyxl默认行为是返回公式的结果)。为了保留公式,我们需要在读取源文件时设置`data_only=False`(默认就是False,所以我们在加载工作簿时如果不设置`data_only`,则默认会加载公式)。但是,当我们使用`cell.value`时,对于公式单元格,我们得到的是公式字符串(以'='开头)?实际上,在openpyxl中,如果加载工作簿时没有设置`data_only=True`,那么公式单元格的`value`是公式字符串(以'='开头)。所以,上述代码在复制时,公式会被当作字符串复制过去,这样在目标工作簿中,公式会被保留。 因此,上述代码对于公式也可以正常复制(因为公式单元格的value就是公式字符串)。 但是,注意:如果源工作簿是保存了公式结果,而我们加载时使用`data_only=True`,那么公式单元格的value就是结果值,公式会丢失。 所以,在加载源工作簿时,不要使用`data_only=True`。 另外,上述代码没有处理图片、图表等对象。如果需要复制图片,可以使用`openpyxl.drawing.image.Image`,但需要遍历工作表中的所有图片,然后添加到新工作表中。这需要额外的代码。 由于问题只要求复制工作表,没有特别说明包含图片,所以我们先按照上述方法实现。 总结:使用手动复制的方法,我们可以将源工作簿中的工作表复制到目标工作簿中。 如果用户需要复制图片,可以进一步扩展该函数。 因此,针对用户的问题,我们给出以下解决方案: 步骤: 1. 定义复制工作表的函数(如上)。 2. 加载源工作簿和目标工作簿。 3. 调用函数复制指定工作表到目标工作簿。 4. 保存目标工作簿。 注意:目标工作簿必须已经存在,如果不存在,我们需要先创建它。 如果目标工作簿不存在,我们可以先创建一个新的工作簿,然后删除默认创建的工作表(如果有),再复制。 或者,我们可以这样: 如果目标工作簿不存在: target_workbook = Workbook() target_workbook.remove(target_workbook.active) # 删除默认的工作表(可选,也可以保留) 然后复制。 完整代码示例(包括目标工作簿不存在的情况): 假设目标文件不存在,则创建新的工作簿;如果存在,则加载。 同时,我们提供更完整的代码: ```python from openpyxl import load_workbook, Workbook from openpyxl.utils import get_column_letter from copy import copy import os def copy_sheet(source_sheet, target_workbook, sheet_name=None): # 如果未指定新工作表名称,则使用源工作表的名称 if sheet_name is None: sheet_name = source_sheet.title # 在目标工作簿中创建新工作表 new_sheet = target_workbook.create_sheet(sheet_name) # 复制合并单元格 for merged_cell in source_sheet.merged_cells.ranges: new_sheet.merge_cells(str(merged_cell)) # 复制行高 for r_idx in source_sheet.row_dimensions: # 注意:row_dimensions的键是行号(整数) new_sheet.row_dimensions[r_idx] = copy(source_sheet.row_dimensions[r_idx]) # 复制列宽 for c_letter in source_sheet.column_dimensions: # 注意:column_dimensions的键是列字母(字符串) new_sheet.column_dimensions[c_letter] = copy(source_sheet.column_dimensions[c_letter]) # 复制每个单元格 for row in source_sheet.iter_rows(): for cell in row: # 创建新单元格 new_cell = new_sheet.cell(row=cell.row, column=cell.column, value=cell.value) if cell.has_style: 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) if cell.protection is not None else None new_cell.alignment = copy(cell.alignment) # 复制其他属性 new_sheet.sheet_properties = source_sheet.sheet_properties new_sheet.page_setup = source_sheet.page_setup new_sheet.page_margins = source_sheet.page_margins new_sheet.print_options = source_sheet.print_options # 注意:这里没有复制图片,如果需要复制图片,需要额外处理 # 复制图片(如果有) # 在openpyxl中,图片在worksheet._images列表中 for image in source_sheet._images: new_sheet.add_image(image) return new_sheet # 源工作簿路径 source_path = 'source.xlsx' # 目标工作簿路径 target_path = 'target.xlsx' # 加载源工作簿 source_workbook = load_workbook(source_path) source_sheet = source_workbook['Sheet1'] # 替换为要复制的工作表名 # 判断目标工作簿是否存在 if os.path.exists(target_path): target_workbook = load_workbook(target_path) else: target_workbook = Workbook() # 删除默认创建的工作表(如果不需要) while target_workbook.sheetnames: target_workbook.remove(target_workbook.active) # 复制工作表 copy_sheet(source_sheet, target_workbook, sheet_name='Copied Sheet') # 保存目标工作簿 target_workbook.save(target_path) ``` 注意:复制图片的部分,我们使用了`source_sheet._images`,这是一个内部属性,在openpyxl3.0.5版本中,可以通过这个属性获取图片。但是,不同版本可能不同。另外,`add_image`方法用于添加图片。 由于openpyxl版本可能有差异,请确保使用较新的版本(如3.0.5以上)。 另外,如果工作表中有其他对象(如图表),则上述代码无法复制。如果需要复制图表,需要更复杂的操作。 因此,根据需求,我们提供了基本数据和样式的复制,并添加了图片的复制。 最后,我们保存目标工作簿。 这样,我们就实现了将一个工作簿中的工作表复制到另一个工作簿。 相关问题: 1. 如何复制工作簿中的所有工作表到另一个工作簿? 2. 复制工作表时如何保留公式引用(特别是跨工作簿引用)? 3. 在openpyxl中如何复制图表? 4. 复制工作表时遇到名称冲突怎么办? 5. 如何复制隐藏的工作表? 我们可以根据这些扩展问题进一步讨论。</think>### 使用 openpyxl 复制工作表到另一个工作簿的解决方案 在 openpyxl 中,将工作表从一个工作簿复制到另一个工作簿需要手动操作,因为 `copy_worksheet()` 方法只能在同一个工作簿内复制工作表[^2]。以下是完整实现步骤: #### 步骤 1:导入必要的库 ```python from openpyxl import load_workbook from copy import copy ``` #### 步骤 2:定义复制工作表的函数 ```python def copy_sheet_to_workbook(source_sheet, target_wb, new_sheet_name=None): """ 将源工作表复制到目标工作簿 :param source_sheet: 要复制的源工作表对象 :param target_wb: 目标工作簿对象 :param new_sheet_name: 新工作表名称(可选) :return: 新创建的工作表对象 """ # 创建新工作表 new_sheet = target_wb.create_sheet(new_sheet_name or source_sheet.title) # 复制单元格数据和样式 for row in source_sheet.iter_rows(): for cell in row: new_cell = new_sheet.cell( row=cell.row, column=cell.column, value=cell.value ) if cell.has_style: 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.alignment = copy(cell.alignment) # 复制列宽 for col_letter, dimension in source_sheet.column_dimensions.items(): new_sheet.column_dimensions[col_letter].width = dimension.width # 复制行高 for row_idx, dimension in source_sheet.row_dimensions.items(): new_sheet.row_dimensions[row_idx].height = dimension.height # 复制合并单元格区域 for merged_range in source_sheet.merged_cells.ranges: new_sheet.merge_cells(str(merged_range)) return new_sheet ``` #### 步骤 3:加载工作簿并执行复制 ```python # 加载源工作簿(包含要复制的工作表) source_wb = load_workbook('source_workbook.xlsx') source_sheet = source_wb['Sheet1'] # 替换为实际工作表名 # 加载目标工作簿(或创建新工作簿) try: target_wb = load_workbook('target_workbook.xlsx') except FileNotFoundError: target_wb = Workbook() # 创建新工作簿 target_wb.remove(target_wb.active) # 删除默认工作表 # 执行复制 new_sheet = copy_sheet_to_workbook( source_sheet=source_sheet, target_wb=target_wb, new_sheet_name="Copied_Sheet" ) # 保存目标工作簿 target_wb.save('target_workbook.xlsx') ``` #### 关键注意事项: 1. **样式复制**:使用 `copy()` 函数确保字体、边框等样式属性被完整复制[^4] 2. **列宽/行高**:必须手动复制列宽和行高设置 3. **合并单元格**:需要单独处理合并单元格区域 4. **公式处理**:公式会被保留,但跨工作簿引用可能需要手动调整 5. **性能优化**:对于大型工作表,建议使用 `write_only=True` 模式加载工作簿 #### 替代方案(使用第三方库) 如需更高效的复制,可考虑使用 `pywin32` (仅Windows): ```python import win32com.client as win32 excel = win32.gencache.EnsureDispatch('Excel.Application') src_wb = excel.Workbooks.Open(r'C:\source.xlsx') tgt_wb = excel.Workbooks.Open(r'C:\target.xlsx') src_wb.Sheets("Sheet1").Copy(Before=tgt_wb.Sheets(1)) tgt_wb.Save() excel.Quit() ``` > **提示**:此方法会直接操作Excel应用程序,复制效果更完整(包括图表等复杂对象),但仅限于Windows环境且需安装Excel
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值