基于python openpyxl库处理excel表格时遇到的问题和总结

  1. 任务
    在这里插入图片描述

需求描述是:将C列的内容(3,4,5)合并在一起之后,将5,6,7三行合并成一行
效果如下:
在这里插入图片描述
2.思路
需求很清晰,简单。但是数据量不是上面这样简单的几行
在这里插入图片描述
在原数据中,我们要合并的内容就是E列(对应上述的C列)。
在A列(通话开始时间)中,有些时间单元格是合并单元格,有些不是,只有对于合并单元格的,它对应的E列才需要我们合并。

所以,先找出A列合并的单元格,对于其同行的E列内容进行合并,最后将相应的行合并为一行

  1. 编码
    ① 找出合并的单元格。excel的合并单元格中,只有单元格的第一行内容是不为空的,其余行内容为空。所以直观思路是遍历A列的单元格,如果行内容为空,就开始记录行数,直到遇到下一个不为空的内容。之间记录的行数就是合并单元格的行数范围。

但是这样有些“呆”。openpyxl给我们提供了现成的方法:

sheet.merged_cells

该方法会返回一个列表,包含了所有合并单元格的范围。我们print输出sheet.merged_cells,结果如下:
在这里插入图片描述
以第一项为例,“A4:A9”,表明A列第四行到第九行是合并单元格。至此第一个目标实现

②将E列的内容合并。
首先我们需要获取合并单元格的行范围,方便后续操作。很简单的一个字符串处理,不赘述。

for crange in sheet.merged_cells:
    cells=str(crange)
    if cells[0]!='A':
        break
    index=cells.find(':')
    start=cells[1:index]
    end=cells[index+2:]

合并单元格,openpyxl给我们提供的方法是sheet.merge_cells('start:end')
但是该方法操作,不等同于合并单元格内容。也就是说,会将若干行单元格合并,同时内容只保留第一行的
所以我们需要遍历start to end,先将每一行内容保存到一起,然后合并单元格后,将内容存回去。

    content=""
    area=sheet[f'E{
      
      start}:E{
      
      end}']
    for i in area:
        for j in i:
            if(j.value!=None):
                content+=(j.value+'\n')
    sheet.merge_cells(f'E{
      
      start}:E{
      
      end}')
    sheet['E'+f'{
      
      start}'].value=content

③将多行合并成一行。
以最开始那张图为例,6/7行是冗余的,我们希望将5,6,7行合并成一行。

最直接的想法是删掉5,6,7行。我们也这样做了。
代码很简单,

sheet.delete_rows(idx=int(start)+1,amount=int(end)-int(start))

但是效果是错的。会发现,有很多单元格莫名其妙地被删掉了,似乎没有任何规律可循

分析之后,结论是:在初始的for循环当中:

for crange in sheet.merged_cells:

**这里的merged_cells的内容是一次生成的,不是每次取出一个crange的时候,就去run一遍sheet.merged_cells,更新一遍。**比如这个list的内容是[1 2 3],那么这个for循环就相当于一个指针在遍历list的内容。

问题就在这里,我们每次取出一个单元格,del掉多余的行之后,后面的行是会顺位补上去的。,也就导致,后面那些合并单元格,其位置会变化。
比如A4:A9是一个合并单元格。A10:A11是一个合并单元格。
将5-9行多余的删去后,10-11行顺位上填充,变成了A5-A6.但是我们的merged_cells并没有变化,所以删的时候还是删的11行,这就导致很多行被莫名其妙删掉。

另外一个问题是由于我们AB,C,D等列的对应内容是合并单元格,所以如果想要del掉多余的列的话,只会del掉E列(5-9)行,(此时我们E列还没有合并的情况下。)
也就是对于A4:A9,直接del 5-9行,ABCD列的行内容不会被删,但是E列的会被删。于是就出现了E列内容错位的问题。

如何规避以上问题?

将E列内容合并后,将所有合并单元格拆解。
然后遍历每一行,如果单元格内容为空,则del掉。

list=[]
for crange in sheet.merged_cells:
    cells=str(crange)
    list.append(cells)
#print(list)

for cell in list:
    sheet.unmerge_cells(cell)

i=1
content=sheet['A']
for cell in content:
    #print(cell.value)
    if cell.value==None:
        sheet.delete_rows(i)
    else:i+=1


猜你喜欢

转载自blog.csdn.net/WessonX/article/details/122374329