0
点赞
收藏
分享

微信扫一扫

【Python小工具】Python小工具批量提取Excel图片

大师的学徒 2023-07-05 阅读 107
pythonexcel

        目前有一个需求,就是批量读取当前目录下所有文件夹里的Excel文件,去获取出Excel文件中的图片,并根据图片对应的行去获取某列的值作为命名方式进行命名,并统一保存在一个新的文件夹里面。

        自己花了几个小时写了一个小工具出来,利用的是openpyxl这个库,其他库用了提取效果不太好,这个提取效果挺不错的。以下代码要根据实际需求,将“货品编码”改成你对应需要的值。如果你不需要命名规则,则直接去掉都行。

第一个版本,针对于不规则分布图片的Excel,进行每个单元格进行遍历,比较费时:

import os
from openpyxl import load_workbook
from openpyxl.drawing.image import Image
from openpyxl_image_loader import SheetImageLoader
from openpyxl.utils.cell import get_column_letter
from PIL import Image

# 创建文件夹
def create_folder():
    if not os.path.exists('images'):
        os.makedirs('images')
    print("成功创建/更新文件夹!")

# 获取当前目录下的文件夹
def get_folders(directory):
    folders = []
    for entry in os.scandir(directory):
        if entry.is_dir():
            folders.append(entry.name)
    print("成功获取当前目录的文件夹!")
    return folders

# 提取图片
def extract_images():
    # 创建存放文件夹
    create_folder()
    # 获取当前目录下的文件夹
    folders = get_folders('.')
    i = 1
    num = 1
    # 遍历当前目录下的文件夹
    for folder in folders:
        print(f"正在遍历第{i}个文件夹{folder}......")
        # 进行提取图片
        num = extract_images_from_excel(folder, num)
        i += 1

# 进行提取图片
def extract_images_from_excel(folder, num):
    # 遍历当前文件夹内的所有文件
    for entry in os.scandir('.\\'+folder):
        # 如果当前对象是文件且后缀是xlsx
        if entry.is_file() and entry.name.endswith('.xlsx'):
            print(f'{folder}下的Excel文件路径为:{entry.path}')
            # 打开当前文件
            wb = load_workbook(entry.path)
            # 获取当前xlsx的所有Sheet表
            worksheets = wb.worksheets
            # 遍历xlsx中每一个Sheet
            for ws in worksheets:
                # 获取当前列名为货品编码的列序号
                code_index = ''
                for column in ws.iter_cols():
                    if column[0].value == "货品编码":
                        code_index = column[0].column
                # 创建图片加载对象
                image_loader = SheetImageLoader(ws)
                # 每一行进行遍历,获取行序号和该行数据
                for row_index, row in enumerate(ws.rows, start=1):
                    # 每一列进行遍历
                    for column_index in range(1, len(row) + 1):
                        # 获取列序号
                        column_letter = get_column_letter(column_index)
                        # 如果当前单元格是图片
                        if image_loader.image_in(f'{column_letter}{row_index}'):
                            # 获取图片
                            image = image_loader.get(f'{column_letter}{row_index}')
                            # 获取图片格式
                            image_type = image.format
                            # 获取当前行的货品编码列的值
                            code = ws.cell(row=(row_index), column=code_index).internal_value
                            # 保存图片(保存命名为 序号_货品编码)
                            print(f'正在提取单元格{column_letter}{row_index + 1}的图片......')
                            image.save(f"./images/{code}_{num}.{image_type}")
                            # 序号递增
                            num += 1
            # 关闭文件对象
            wb.close()
    return num


if __name__ == '__main__':
    print("此版本是针对于图片分布不规则的情况,提取图片速度尚且较慢")
    print("开始提取......")
    # 提取图片
    extract_images()
    print("提取完成!")

第二个版本,针对于某一列统一分布图片的Excel,只会进行有图片那一列的遍历,比较快速:

import os
from openpyxl import load_workbook
from openpyxl.drawing.image import Image
from openpyxl_image_loader import SheetImageLoader
from openpyxl.utils.cell import get_column_letter
from PIL import Image

# 创建文件夹
def create_folder():
    if not os.path.exists('images'):
        os.makedirs('images')
    print("成功创建/更新文件夹!")

# 获取当前目录下的文件夹
def get_folders(directory):
    folders = []
    for entry in os.scandir(directory):
        if entry.is_dir():
            folders.append(entry.name)
    print("成功获取当前目录的文件夹!")
    return folders

# 提取图片
def extract_images():
    # 创建存放文件夹
    create_folder()
    # 获取当前目录下的文件夹
    folders = get_folders('.')
    i = 1
    num = 1
    # 遍历当前目录下的文件夹
    for folder in folders:
        print(f"正在遍历第{i}个文件夹{folder}......")
        # 进行提取图片
        num = extract_images_from_excel(folder, num)
        i += 1

# 进行提取图片
def extract_images_from_excel(folder, num):
    # 遍历当前文件夹内的所有文件
    for entry in os.scandir('.\\'+folder):
        # 如果当前对象是文件且后缀是xlsx或者xls
        if entry.is_file() and (entry.name.endswith('.xlsx') or entry.name.endswith('.xls')):
            print(f'{folder}下的Excel文件路径为:{entry.path}')
            # 打开当前文件
            wb = load_workbook(entry.path)
            # 获取当前xlsx的所有Sheet表
            worksheets = wb.worksheets
            # 遍历xlsx中每一个Sheet
            for ws in worksheets:
                # 获取当前列名为货品编码的列序号
                code_index = ''
                for column in ws.iter_cols():
                    if column[0].value == "货品编码":
                        code_index = column[0].column
                # 创建图片加载对象
                image_loader = SheetImageLoader(ws)
                # 记录第一次遍历的标志
                img_sign_index = ''
                # 每一行进行遍历,获取行序号和该行数据
                for row_index, row in enumerate(ws.rows, start=1):
                    # 只有第一次才会进行每列遍历,去找到图片所在的列
                    if img_sign_index == '':
                        # 每一列进行遍历
                        for column_index in range(1, len(row) + 1):
                            # 获取列序号
                            column_letter = get_column_letter(column_index)
                            if image_loader.image_in(f'{column_letter}{row_index}'):
                                # 获取对应图片的列序号
                                img_sign_index = column_letter
                                break
                    # 如果不为空,则证明有图片,反之直接跳过
                    if img_sign_index != '':
                        # 后面遍历直接去找图片所在的列
                        image = image_loader.get(f'{img_sign_index}{row_index}')
                        # 获取图片格式
                        image_type = image.format
                        # 获取当前行的货品编码列的值
                        code = ws.cell(row=(row_index), column=code_index).internal_value
                        # 保存图片(保存命名为 序号_货品编码)
                        print(f'正在提取单元格{img_sign_index}{row_index + 1}的图片......')
                        image.save(f"./images/{num}_{code}.{image_type}")
                        # 序号递增
                        num += 1

            # 关闭文件对象
            wb.close()
    return num


if __name__ == '__main__':
    print("此版本是针对于图片集中分布在一列的情况,能更快提取图片出来")
    print("开始提取......")
    # 提取图片
    extract_images()
    print("提取完成!")

注释都比较清楚,但是现在有一个问题就是,image_loader.get()方法只能提取包含于单元格内的图片,不能获取在单元格边缘有溢出的图片,目前还没有去解决这个问题,之后解决后会更新。

2023-07-03 更新

此版本不是遍历单元格,是直接找图片,再锁定图片的中心行位置去找相应的货品编码,效率更高,而且不会因为图片位于单元格边缘存在识别不到的问题。

import os

from openpyxl import load_workbook
import os
from openpyxl_image_loader import SheetImageLoader
from openpyxl.utils.cell import get_column_letter

# 创建文件夹
def create_folder():
    if not os.path.exists('images'):
        os.makedirs('images')
    print("成功创建/更新文件夹!")

# 获取当前目录下的文件夹
def get_folders(directory):
    folders = []
    for entry in os.scandir(directory):
        if entry.is_dir():
            folders.append(entry.name)
    print("成功获取当前目录的文件夹!")
    return folders

# 提取图片
def extract_images():
    # 创建存放文件夹
    create_folder()
    # 获取当前目录下的文件夹
    folders = get_folders('.')
    i = 1
    num = 1
    # 遍历当前目录下的文件夹
    for folder in folders:
        print(f"正在遍历第{i}个文件夹{folder}......")
        # 进行提取图片
        num = extract_images_from_excel(folder, num)
        i += 1

# 进行提取图片
def extract_images_from_excel(folder, num):
    # 遍历当前文件夹内的所有文件
    for entry in os.scandir('.\\'+folder):
        # 如果当前对象是文件且后缀是xlsx或者xls
        if entry.is_file() and entry.name.endswith('.xlsx'):
            print(f'{folder}下的Excel文件路径为:{entry.path}')
            # 打开当前文件
            wb = load_workbook(entry.path)
            # 遍历每一个Sheet
            for sheet_name in wb.sheetnames:
                sheet = wb[sheet_name]
                image_loader = SheetImageLoader(sheet)
                # 获取当前列名为货品编码的列序号
                code_index = ''
                for column in sheet.iter_cols():
                    if column[0].value == "货品编码":
                        code_index = column[0].column
                # 遍历Sheet中的所有图片
                for image in sheet._images:
                    # 获取图片中心行数,判断货品编码是哪一个
                    row_index = (int(((image.anchor._from.row + 1) + (image.anchor.to.row + 1)) / 2))
                    # 获取当前行的货品编码列的值(取中间值)
                    code = sheet.cell(row=row_index, column=code_index).value
                    # 获取图片格式
                    img_format = image.format
                    # 重新将图片获取出来(因为获取下标这个image没有存储方法),直接通过定位左上角坐标将图片取出来
                    img = image_loader.get(f'{get_column_letter(image.anchor._from.col + 1)}{image.anchor._from.row + 1}')
                    # 保存图片
                    print(f'正在提取货品编码为{code}的图片{image}......')
                    img.save(f'./images/{num}_{code}.{img_format}')
                    # 序号递增
                    num += 1

            # 关闭文件对象
            wb.close()
    return num

# v1.0:此版本是针对于图片分布不规则的情况,提取图片速度尚且较慢
# v1.1:此版本是针对于图片集中分布在一列的情况,能更快提取图片出来。
# v1.2:此版本解决图片位于Excel边界时存在的问题,只要图片中心行在这一行,就可以匹配相应的国家编码,同时不用去遍历,直接获取图片。
if __name__ == '__main__':
    print("开始提取......")
    # 提取图片
    extract_images()
    print("提取完成!")

 

举报

相关推荐

0 条评论