需求
当有一张如下图所示的excel表,一列是图片,另一列是图片对应的名称(如型号)。如何把里面的图片批量下载下来并按对应列的单元格命名呢?
初步方案
这类问题,网上有烂大街解决方案。将excel的后缀改为zip或者rar,变成压缩文件。再解压,在xlmedia文件夹下将得到excel里所有的图片。
这个过程,在Python里可以这么做:
代码语言:javascript复制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库。核心代码如下:
代码语言:javascript复制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编辑器,接着输入以下代码。该代码比较详细地考虑了图片输出的各种场景,详解代码里的中文注释。最后点击编辑器上方的运行就会实现我们的需求。
代码语言:javascript复制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
最后
欢迎关注讨论,期待一起分享更多的解决方法。后台回复“图片提取”可获得实用小工具。