Python操作Excel插入删除行的方法
本文介绍Python处理Excel时插入和删除行的两种方案。openpyxl虽常用但中间操作性能差,需转换列表或修改源码;推荐xlwings,支持调用VBA接口,在Windows和macOS下运行高效且兼容WPS,适合需要动态调整行数的场景。
1. 前言
近期因任务需求,需编写一个处理Excel的脚本。主要功能是对A表格中某一列的字符串进行分组和排序,随后依据排序后的A表格数据,按固定格式自动填充至B表格。
在脚本开发初期,查阅了大量资料,最初选择了openpyxl模块。该模块使用便捷,已成功用于重新填写A表格的指定列。然而后续发现,任务涉及删除和插入空白行的操作,而openpyxl仅支持在表格末尾追加新行,无法直接在中间位置插入或删除行,这成为了使用瓶颈。
在搜索过程中,发现网上有关于使用openpyxl实现插入和删除行的讨论,现对这些方法进行整理。
2. 使用openpyxl
一种思路是将Sheet数据转换为列表,在列表中进行操作后再写回。该方法理论上可行,但实际测试表明运行速度极慢。当数据量超过1000条时,耗时已无法接受。
# Creat insert row function group----------------------------------------------
def blankRowInsert(sheet, row_num, add_num):
myList = Sheet2List(sheet)
insertLine(myList, row_num, add_num, sheet.max_column)
List2Sheet(sheet,myList)
def Sheet2List(sheet):
# 把一个表格中的数据全部导出到一个列表
listResult = []
for i in range(1,sheet.max_row + 1):
lineData = []
for j in range(1,sheet.max_column +1):
cell = sheet.cell(row = i, column = j)
lineData.append(cell.value)
listResult.append(lineData)
return listResult
def insertLine(aList, row_num , add_num, maxColumn):
# 对列表进行添加操作操作
for _ in range(1,add_num + 1):
# ['']*N是创建一个个数为N的空格列表,插入列表aList
aList.insert(row_num, [''] * maxColumn)
def List2Sheet(sheet,list):
# 把数据写回sheet
for i in range(1, len(list) + 1):
for j in range(1, len(list[0]) + 1):
cell = sheet.cell(row=i, column=j)
cell.value = list[i-1][j-1]
# End of insert row function group---------------------------------------------
另一种思路是直接扩展openpyxl的功能,添加新方法。笔者未对此进行试验,以下代码源自StackOverflow相关问题的解答,有兴趣的读者可自行尝试。
def insert_rows(self, row_idx, cnt, above=False, copy_style=True, fill_formulae=True):
"""Inserts new (empty) rows into worksheet at specified row index.
:param row_idx: Row index specifying where to insert new rows.
:param cnt: Number of rows to insert.
:param above: Set True to insert rows above specified row index.
:param copy_style: Set True if new rows should copy style of immediately above row.
:param fill_formulae: Set True if new rows should take on formula from immediately above row, filled with references new to rows.
Usage:
* insert_rows(2, 10, above=True, copy_style=False)
"""
CELL_RE = re.compile("(?P<col>\$?[A-Z]+)(?P<row>\$?\d+)")
row_idx = row_idx - 1 if above else row_idx
def replace(m):
row = m.group('row')
prefix = "$" if row.find("$") != -1 else ""
row = int(row.replace("$",""))
row += cnt if row > row_idx else 0
return m.group('col') + prefix + str(row)
# First, we shift all cells down cnt rows...
old_cells = set()
old_fas = set()
new_cells = dict()
new_fas = dict()
for c in self._cells.values():
old_coor = c.coordinate
# Shift all references to anything below row_idx
if c.data_type == Cell.TYPE_FORMULA:
c.value = CELL_RE.sub(
replace,
c.value
)
# Here, we need to properly update the formula references to reflect new row indices
if old_coor in self.formula_attributes and 'ref' in self.formula_attributes[old_coor]:
self.formula_attributes[old_coor]['ref'] = CELL_RE.sub(
replace,
self.formula_attributes[old_coor]['ref']
)
# Do the magic to set up our actual shift
if c.row > row_idx:
old_coor = c.coordinate
old_cells.add((c.row,c.col_idx))
c.row += cnt
new_cells[(c.row,c.col_idx)] = c
if old_coor in self.formula_attributes:
old_fas.add(old_coor)
fa = self.formula_attributes[old_coor].copy()
new_fas[c.coordinate] = fa
for coor in old_cells:
del self._cells[coor]
self._cells.update(new_cells)
for fa in old_fas:
del self.formula_attributes[fa]
self.formula_attributes.update(new_fas)
# Next, we need to shift all the Row Dimensions below our new rows down by cnt...
for row in range(len(self.row_dimensions)-1+cnt,row_idx+cnt,-1):
new_rd = copy.copy(self.row_dimensions[row-cnt])
new_rd.index = row
self.row_dimensions[row] = new_rd
del self.row_dimensions[row-cnt]
# Now, create our new rows, with all the pretty cells
row_idx += 1
for row in range(row_idx,row_idx+cnt):
# Create a Row Dimension for our new row
new_rd = copy.copy(self.row_dimensions[row-1])
new_rd.index = row
self.row_dimensions[row] = new_rd
for col in range(1,self.max_column):
col = get_column_letter(col)
cell = self.cell('%s%d'%(col,row))
cell.value = None
source = self.cell('%s%d'%(col,row-1))
if copy_style:
cell.number_format = source.number_format
cell.font = source.font.copy()
cell.alignment = source.alignment.copy()
cell.border = source.border.copy()
cell.fill = source.fill.copy()
if fill_formulae and source.data_type == Cell.TYPE_FORMULA:
s_coor = source.coordinate
if s_coor in self.formula_attributes and 'ref' not in self.formula_attributes[s_coor]:
fa = self.formula_attributes[s_coor].copy()
self.formula_attributes[cell.coordinate] = fa
# print("Copying formula from cell %s%d to %s%d"%(col,row-1,col,row))
cell.value = re.sub(
"(\$?[A-Z]{1,3}\$?)%d"%(row - 1),
lambda m: m.group(1) + str(row),
source.value
)
cell.data_type = Cell.TYPE_FORMULA
# Check for Merged Cell Ranges that need to be expanded to contain new cells
for cr_idx, cr in enumerate(self.merged_cell_ranges):
self.merged_cell_ranges[cr_idx] = CELL_RE.sub(
replace,
cr
)
# Use way:
# Worksheet.insert_rows = insert_rows
3. 使用xlwings
经过一系列尝试和折腾后,笔者放弃了使用openpyxl进行Excel行的插入和删除操作。在网上搜索后,发现了xlwings模块。其文档说明支持调用VBA函数,这颇具吸引力。查阅文档后,决定采用该模块,并附上笔者编写的几段代码作为使用示例。
3.1. 删除行: range.api.EntireRow.Delete()
# Delete origin row
temp_del = 0
if len(delete_list) > 0:
for delete_row in delete_list:
# Report schedule
print("Have alerady done: " + \
str((temp_del*100)//delete_num) + "%")
# Delete one row
wb_sheet.range('A'+str(delete_row-temp_del)).api.EntireRow.Delete()
temp_del = temp_del + 1
wb.save()
上述代码运用了一些技巧。delete_list存储了原表格中需要删除的行号。在删除过程中,由于总行数随之减少,因此需要将绝对行号转换为相对行号进行标记删除,temp_del变量的作用即在于此。
3.2. 插入行: sheet.api.Rows(row_number).Insert()
if key_word == sheet.range('A'+str(i_row+1)).value:
# Insert new line
sheet.api.Rows(i_row+2).Insert()
需要注意的是,该VBA函数是向上插入空行。此外,xlwings目前仅支持Windows和macOS系统,暂不支持Linux。不过,xlwings的运行速度远快于openpyxl,且能直接调用VBA函数,对WPS和Excel均兼容。综合来看,选择xlwings更为合适。
以上关于Python操作Excel插入删除行的方法即为全部内容,希望能对各位有所帮助,也欢迎大家多多支持本站。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













