最近的需求是写脚本进行excel的文档批处理。替换某个单元格的公式,内容等。
一开始用的是openpyxl库。遇到的坑是,脚本执行后excel内的图片全部丢失。
查到的解决方式为:
pip install pillow
安装图片相关的库就可以。确实大部分的图片都正常保存下来了。
但是出现了新的坑。运行的时候会出现告警
\openpyxl\reader\drawings.py:59: UserWarning: wmf image format is not supported so the image is being dropped
warn(msg)
导致一部分内容丢失。一开始一直在查找怎么处理wmf文件。但是感觉处理都很麻烦。因为报错是在load_workbook这一步,所以查到的那些在加载完成后把wmf格式的图片修改为其他格式之类的操作根本无法完成。
后面尝试过用pandas。但是pandas更多用于数据的清洗,分析。并不适合用来修改excel。用pandas处理文件会导致原来的各种格式都没了,只剩下单纯的数据。
最后的解决办法是用xlwings库。这个库可以正常打开,处理和保存excel。不会丢失任何的图片。
之后我去看文档到底哪里有wmf图片。发现是一个Group,在openpyxl库内会被识别为wmf文件。
这个Group 113是几个矩形组成的,用于每层文档审核人插入签名图片,内部包含很多小的Group和rectangle。
我的下一个需求刚好是做一个一键插入签名图片的脚本。签名图片需要插入到Group 113内的Group 88内。
import os
import xlwings
app = xw.App(visible=False, add_book=False) # 程序可见,只打开不新建工作薄
app.display_alerts = False # 警告关闭
app.screen_updating = False # 屏幕更新关闭
try:
workbook = app.books.open(path2)
sheet = workbook.sheets['sheet名字']
for cell in sheet.shapes:
print(cell.name)
workbook.close()
app.quit() # 关闭程序
os.system("pause")
except Exception as e:
print(e)
app.quit() # 关闭程序
os.system("pause")
放置图片的那个shape名字叫Group 88,是这个Group 113的一部分。我发现遍历shapes是遍历不出来这个小group的,但是我直接调用这个名字,可以打印出来他的shape部分信息。就很迷惑。
最后是调用Ungroup,将这个组合取消合并,就可以打印出所有的shapes了。
group = sheet.shapes['Group 113']
group.api.Ungroup()
cr = sheet.shapes['Group 88'] #承认
print(cr.left, cr.top, cr.width, cr.height)
sheet.pictures.add('xx.png', left=cr.left, top=cr.top, width=cr.width, height=cr.height)
这里是插入图片的流程。我是将图片插入在Group113内的Group88内。在添加的时候指定位置和大小。
执行完毕后图片插入是成功的,大小也正常。但是文档中的对象变成了很多个,原本整个Group是可以随意拖拽,保持形状整体移动的,现在是单个分散的shapes,一拖动就散架了。
这个将Ungroup后的shapes重新Group的过程,找了很久才找到正确的函数。跟chatgpt斗智斗勇很多轮。
groupMem = []
for cell in sheet.shapes:
if(cell.type != 'picture' and cell.type != "comment"):
groupMem.append(cell.name)
print(groupMem)
newGroup = sheet.api.Shapes.Range(groupMem).Group()
newGroup.Name = 'Group 113'
这样就可以实现想要的需求。判断条件根据自己的需求做对应更改。
注意api需要在sheet和Shapes中间,
注意Shapes的S大写,
注意Range的R大写。
注意Group在最后。
注意重命名时Name的N也是大写。
chatgpt建议我用了VBA宏等各种方式,还有sheet.shapes.api.group(groupMem),等各种函数。
每次都报错。api的位置,group的位置,range的有无,各种坑都踩过。用VBA宏也各种报错。
只有上面这个能实现。
最后贴一下完整代码。我这边是批处理路径下所有表格的某个sheet。需要根据实际需求做对应修改。
import os
import xlwings
import xlwings as xw
retval = os.getcwd()
print("当前工作目录为 %s" % retval)
app = xw.App(visible=False, add_book=False) # 程序可见,只打开不新建工作薄
app.display_alerts = False # 警告关闭
app.screen_updating = False # 屏幕更新关闭
try:
#多层的for循环可能有些冗余,可自行修改。写好能用我就没改
for root, ds, fs in os.walk(retval):
os.chdir(retval)
for d in ds:
path = os.path.join(root, d)
for root1, ds1, fs1 in os.walk(path):
for f1 in fs1:
if(f1.endswith('.xlsx') and f1.startswith('~') == False):
path2 = os.path.join(root1, f1)
print(path2)
workbook = app.books.open(path2)
sheet = workbook.sheets['sheetName']
group = sheet.shapes['Group 113']
group.api.Ungroup()
cr = sheet.shapes['Group 88']
print(cr.left, cr.top, cr.width, cr.height)
sheet.pictures.add('xx.png', left=cr.left, top=cr.top, width=cr.width, height=cr.height)
groupMem = []
for cell in sheet.shapes:
#判断条件注意修改
if(cell.type != 'picture' and cell.type != "comment"):
groupMem.append(cell.name)
print(groupMem)
newGroup = sheet.api.Shapes.Range(groupMem).Group()
newGroup.Name = 'Group 113'
workbook.save()
workbook.close()
break
app.quit() # 关闭程序
os.system("pause")
except Exception as e:
print(e)
app.quit() # 关闭程序
os.system("pause")
OK,结束。这几天脚本写下来,觉得xlwings才是最好用的python处理excel的库。