需求:当有一张如下图所示的excel表,一列是图片,另一列是图片对应的名称(如型号)。如何把里面的图片批量下载下来并按对应列的单元格命名呢?
这个过程,在Python里可以这么做:
import os import zipfile # 判断是否是文件和判断文件是否存在 def isfile_exist(file_path): if not os.path.isfile(file_path): print("It's not a file or no such file exist ! %s" % file_path) return False else: return True # 修改指定目录下的文件类型名,将excel后缀名修改为.zip def change_file_name(file_path, new_type='.zip'): if not isfile_exist(file_path): return '' extend = os.path.splitext(file_path)[1] # 获取文件拓展名 if extend != '.xlsx' and extend != '.xls': print("It's not a excel file! %s" % file_path) return False file_name = os.path.basename(file_path) # 获取文件名 new_name = str(file_name.split('.')[0]) + new_type # 新的文件名,命名为:xxx.zip dir_path = os.path.dirname(file_path) # 获取文件所在目录 new_path = os.path.join(dir_path, new_name) # 新的文件路径 if os.path.exists(new_path): os.remove(new_path) os.rename(file_path, new_path) # 保存新文件,旧文件会替换掉 return new_path # 返回新的文件路径,压缩包 # 解压文件 def unzip_file(zipfile_path): if not isfile_exist(zipfile_path): return False if os.path.splitext(zipfile_path)[1] != '.zip': print("It's not a zip file! %s" % zipfile_path) return False file_zip = zipfile.ZipFile(zipfile_path, 'r') file_name = os.path.basename(zipfile_path) # 获取文件名 zipdir = os.path.join(os.path.dirname(zipfile_path), str(file_name.split('.')[0])) # 获取文件所在目录 for files in file_zip.namelist(): file_zip.extract(files, os.path.join(zipfile_path, zipdir)) # 解压到指定文件目录 file_zip.close() return True # 读取解压后的文件夹,打印图片路径 def read_img(zipfile_path): if not isfile_exist(zipfile_path): return False dir_path = os.path.dirname(zipfile_path) # 获取文件所在目录 file_name = os.path.basename(zipfile_path) # 获取文件名 pic_dir = 'xl' + os.sep + 'media' # excel变成压缩包后,再解压,图片在media目录 pic_path = os.path.join(dir_path, str(file_name.split('.')[0]), pic_dir) file_list = os.listdir(pic_path) for file in file_list: filepath = os.path.join(pic_path, file) print(filepath) # 组合各个函数 def compenent(excel_file_path): zip_file_path = change_file_name(excel_file_path) if zip_file_path != '': if unzip_file(zip_file_path): read_img(zip_file_path) # main if __name__ == '__main__': compenent('C:/Users/dell/Desktop/XXX.xlsx')压缩再解压虽然一次性得到了图片,但是该图片命名方式(“image”+序号)并不是我们所想要的。更麻烦的是,图片的序号跟excel表里图片的顺序并不一致,而且是乱序的,导致不能使用批量重命名的手法修改该图片名称。
换言之,这种烂大街的方法只解决了我们问题的一半,而且剩下那半还很不容顺下去解决。有效的解决方案请看下文!
解决方案
Python法:
既然上述压缩再解压的方法不行,Python要换个思路。引用openpyxl库。核心代码如下:
import openpyxl from openpyxl_image_loader import SheetImageLoader import tkinter as tk from tkinter import filedialog # 制作获取文件的弹窗 root = tk.Tk() root.wm_attributes('-topmost',1) # 弹窗置顶 root.withdraw() file_path = filedialog.askopenfilename() # 获取 excel 路径 # 加载excel表和图片 pxl_doc = openpyxl.load_workbook(file_path) sheet = pxl_doc[Sheet] # excel的Sheet名 image_loader = SheetImageLoader(sheet) # 用pd获取图片所在列的起止行号list——ls, 此处省略代码 # 用pd获取图片名称所在列list——image_name,此处省略代码 for i in range(len(ls)): #get the image (put the cell you need instead of 'A1') image = image_loader.get("C"+str(ls[i])) # 假设图片在C列 image.save('/文件夹/'+image_name[i]+'.jpg')VBA方法: 首先,把excel另存为xlsm格式,然后同时按alt和F11调出VBA编辑器,接着输入以下代码。该代码比较详细地考虑了图片输出的各种场景,详解代码里的中文注释。最后点击编辑器上方的运行就会实现我们的需求。
Sub 输出图片并重命名() '做一个文件搜索框,让用户选择输出文件夹 OpenFile = Application.GetOpenFilename("请选择任一文件后按确定(*.*),*.*", , "选择任一文件确定图片输出文件夹,或取消获得当前文件所在文件夹。") If OpenFile = False Then myDir = ThisWorkbook.Path & "\" Else myDir = Left(OpenFile, InStrRev(OpenFile, "\")) End If '判断图片名称列是在图片列的左边还是右边 k = InputBox("1=列左,2=列右,3=上一行,4=下一行,取消=图片所在单元格或无名称", "选择图片名称位置:", 2) If k = 1 Then r = 0: c = -1 ElseIf k = 2 Then r = 0: c = 1 ElseIf k = 3 Then r = -1: c = 0 ElseIf k = 4 Then r = 1: c = 0 End If '设置导出图片的尺寸 k = MsgBox("Yes=按原尺寸,No=按新设定,Cancel=按现在显示", vbYesNoCancel, "输出图片尺寸大小选择") For Each p In ActiveSheet.Shapes ph = p.Height pw = p.Width On Error Resume Next pn = p.TopLeftCell.Offset(r, c).Value If Err.Number <> 0 Then Err.Clear If pn = "" Then n = n + 1: pn = Left(ThisWorkbook.Name, InStrRev(ThisWorkbook.Name, ".") - 1) & Format(n, "000") '图片重命名 GetPicName: On Error Resume Next p.Name = pn & ".jpg" If Err.Number <> 0 Then Err.Clear pn = InputBox("图片名称重复,请重新命名", "图片名称重复", pn) GoTo GetPicName End If p.Select If k = vbYes Then Selection.Copy ActiveSheet.PasteSpecial Format:="图片 (JPEG)" '英文版为"Picture (JPEG)" Selection.Name = "myPic" ElseIf k = vbNo Then Selection.ShapeRange.LockAspectRatio = msoFalse f = InputBox("放大缩小比率", "图片尺寸设定", 2) If IsNumeric(f) And f > 0 Then Selection.ShapeRange.Height = ph * f Selection.ShapeRange.Width = pw * f Else Selection.ShapeRange.Height = InputBox("重新设定图片高度", "图片高尺寸设定", ph) Selection.ShapeRange.Width = InputBox("重新设定图片宽度", "图片宽尺寸设定", pw) End If End If '图片导出 Selection.CopyPicture With ActiveSheet.ChartObjects.Add(0, 0, Selection.Width + 5, Selection.Height + 5).Chart .Paste .Export myDir & p.Name, "JPG" .Parent.Delete End With If k = vbYes Then ActiveSheet.Shapes("myPic").Delete ElseIf k = vbNo Then p.Height = ph p.Width = pw End If p.TopLeftCell.Offset(r, c).Select Next End Sub
VBA代码,有需要的同学自己学习研究一下
Sub 导出图片() On Error Resume Next MkDir ThisWorkbook.Path & "\图片" For Each pic In ActiveSheet.Shapes If pic.Type = 13 Then RN = pic.TopLeftCell.Offset(0, -1).Value pic.Copy With ActiveSheet.ChartObjects.Add(0, 0, pic.Width, pic.Height).Chart '创建图片 .Parent.Select .Paste .Export ThisWorkbook.Path & "\图片\" & RN & ".jpg" .Parent.Delete End With End If Next End Sub