如何批量获取excel的图片并准确命名?

2021-08-31 16:34:33 浏览数 (1)

需求

当有一张如下图所示的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

最后

欢迎关注讨论,期待一起分享更多的解决方法。后台回复“图片提取”可获得实用小工具。

0 人点赞