1. 程式人生 > >python+xlsxwriter+PIL自動壓圖貼圖到Excel小工具

python+xlsxwriter+PIL自動壓圖貼圖到Excel小工具

圖像 設置圖 寫入 {} read shrink conf python ace

一、環境

windows10/mac + python3.6

python第三方庫 xlsxwriter、PIL、argparse

二、需求

1、運行每條測試case成功與否都需要把截圖放在img文件夾裏;

2、把 (平臺)_img 文件夾中的圖片壓縮寫到small_img文件夾裏;

3、根據圖片命名規則,順序寫入所屬case number對應行,順序寫入每條case所有截圖;

4、根據平臺來貼到excel最合適的位置;

5、最重要一點,是給媳婦寫的,提升工作效率;

三、文件樹示例

技術分享圖片

三、Paste_pictures.py

#!/usr/bin/env python3
# coding=utf-8 import xlsxwriter import datetime import os import logging LOGGER = logging.getLogger(__name__) def write_img_for_xls(file_name="test.xlsx", img_dir_name="img", sheet_name="案例截圖", set_img_row=210.75, set_img_column=23.38, x_scale=0.14, y_scale=0.14, ): """
讀取同目錄img文件夾下的所有圖片,格式支持png\jpg\bmp。 圖片必須遵從 1-1.png、1-2.png、2-1.png、2-2.png格式。 註意:是將圖片寫入新的excel文件,如果老的excel中有數據,將會替換所有數據。 file_name: 要寫入的xlsx文件名 img_dir_name: 圖片存放的目錄,必須與腳本在同一目錄下 set_img_row:設置行高 set_img_column:設置列寬 x_scale:圖片寬度縮放比例 y_scale:圖片高度縮放比例 :return: Nothing
""" start_time = datetime.datetime.now() xls_path = os.path.join(os.getcwd(), file_name) if not os.path.isfile(xls_path): raise MyException("what?你居然不把{}文件跟腳本放在同一個目錄下!".format(file_name)) img_path = os.path.join(os.getcwd(), img_dir_name) if not os.path.isdir(img_path): raise MyException("what?你都沒有{}文件夾,我咋給你貼圖啊~".format(img_dir_name)) all_img = os.listdir(img_path) if not all_img: raise MyException("忽悠我呢?{}文件夾下啥都沒有~".format(img_dir_name)) w_book = xlsxwriter.Workbook(xls_path) img_sheet = w_book.add_worksheet(name=sheet_name) count_num = 0 try: for unit_img in all_img: try: img_num = unit_img.split("-") row = int(img_num[0]) column = int(img_num[1].split(".")[0]) suffix = (unit_img.split(".")[1]) if column == 0: LOGGER.warning("圖片名稱格式有誤直接略過!錯誤文件名:{},“-”前後數字必須從1開始!".format(unit_img)) continue except ValueError: LOGGER.warning("[-]圖片命名規則有誤直接略過!錯誤文件名是:{}".format(unit_img)) continue LOGGER.info(">> 正在貼圖到第{}條用例,第{}列...".format(row, column)) img_sheet.set_column(firstcol=0, lastcol=0, width=8.38) img_sheet.write(row - 1, 0, "案例{}".format(row)) small_img = os.path.join(os.getcwd(), "{}/{}-{}.{}".format(img_dir_name, row, column, suffix)) img_sheet.set_column(firstcol=column, lastcol=column, width=set_img_column) img_sheet.set_row(row=row - 1, height=set_img_row) img_config = { x_scale: x_scale, y_scale: y_scale } result = img_sheet.insert_image(row - 1, column, small_img, img_config) img_sheet.write_url(row - 1, column + 1, url="https://my.oschina.net/medivhxu/blog/1590012") if not result: LOGGER.info("[+] 寫入成功!") count_num += 1 else: LOGGER.error("[-] 寫入失敗!") except Exception as e: raise MyException("受到不明外力襲擊,程序...掛了....\n{}".format(e)) finally: try: w_book.close() except PermissionError: raise MyException("你開著{}文件我讓我咋寫。。。趕緊關了!".format(file_name)) LOGGER.info("--------------貼圖完成--------------") LOGGER.info("程序貼圖數量:{},貼圖成功數量:{},貼圖失敗數量:{}".format(len(all_img), count_num, len(all_img) - count_num)) class MyException(Exception): pass write_img_for_xls()

技術分享圖片技術分享圖片

四、run

技術分享圖片

五、result

技術分享圖片

價值:

手動貼圖需要半小時?1小時?貼錯了?不,這些我都不要,僅需不到5秒,全部搞定,然後就可以幹點想幹的事~

性能分析:

其實貼圖並不需要4秒+,因為xlsxwriter這個模塊是自動創建cell的,但是這不是主要原因,主要原因是因為圖片太大了,所以保存時間會隨著圖片加載到內存而線性增長(圖片較大或過多,容易導致腳本直接崩潰),優化方式是選用openpyxl模塊和最中要的圖片預處理。

六、PIL圖片壓縮

1、code

#!/usr/bin/env python3
# coding=utf-8

import os
from PIL import Image


def compression_img(read_dir_name="img", save_img_file=True, height_shrink_multiple=2, width_shrink_multiple=2):
    ‘‘‘
    自動壓縮指定文件夾下的所有圖片
    
    :param read_dir_name: 指定讀取圖片的文件夾名稱,必須在當前目錄下
    :param save_img_file: 是否保存壓縮後的圖片
    :param height_shrink_multiple: 設置圖片壓縮高度的倍數
    :param width_shrink_multiple: 設置圖片壓縮寬度的倍數
    :return: nothing
    ‘‘‘

    img_path = os.path.join(os.getcwd(), read_dir_name)
    all_img = os.listdir(img_path)
    for unit_img in all_img:
        try:
            img_num = unit_img.split("-")
            row = int(img_num[0])
            column = int(img_num[1].split(".")[0])
            suffix = (unit_img.split(".")[1])
            if column == 0:
                print("圖片名稱格式有誤直接略過!錯誤文件名:{},“-”前後數字必須從1開始!".format(unit_img))
                continue
        except ValueError:
            print("[-]圖片命名規則有誤直接略過!請參考1-1.png格式從新運行或手動解決!")
            print("錯誤文件名是:{}".format(unit_img))
            continue

        img_fp = os.path.join(img_path, unit_img)
        origin_img = Image.open(img_fp)
        w, h = origin_img.size
        small_img = origin_img.resize((int(w / height_shrink_multiple), int(h / width_shrink_multiple)))
        if save_img_file:
            img_save_fp = os.path.join(os.getcwd(), "small_img")
            if os.path.isdir(os.path.join(os.getcwd(), "small_img")):
                print("warning, 已有small_img文件夾!直接保存到裏面了!")
                small_img.save(os.path.join(img_save_fp, ("{}-{}.{}".format(row, column, suffix))))
            else:
                os.mkdir("small_img")
                print("新建文件夾“small_img”,壓縮後的圖片將存儲在該文件夾中。")
                small_img.save(os.path.join(img_save_fp, ("{}-{}.{}".format(row, column, suffix))))
        print(">> 正在處理圖像{}-{}.{},原像素高和寬{},壓縮後的高和寬{}".format(row, column, suffix,
                                                            (w, h), small_img.size))
        small_img.close()
    print("--------------圖片壓縮完成--------------")


compression_img()

2、再次運行Paste_pictures.py

技術分享圖片

可以明顯看出,保存文件的時間有非常顯著的提升。

七、模塊化

把以上兩個功能合並,增加平臺類型,根據需求增加了3個平臺的圖片縮放比例和寬高,增加運行log,增加作為主程序命令行運行。以後如果擴展的話直接調用或者寫個類,增加幾個返回值就可以了。

#!/usr/bin/env python3
# coding=utf-8

import xlsxwriter
import datetime
import os
import argparse
import logging
from PIL import Image

LOGGER = logging.getLogger(__name__)

PLATFORM = {"phone": {
    "x_scale": 0.29,
    "y_scale": 0.29,
    "width": 23.38,
    "height": 210.75
},
    "pad": {
        x_scale: 0.2,
        "y_scale": 0.2,
        "width": 58,
        "height": 230
    },
    "oppo": {
        x_scale: 0.29,
        y_scale: 0.3,
        "width": 22.17,
        "height": 230
    }
}


def _check_os_dir(read_dir_name, small_dir_name="small", xlsx_file_name="test.xlsx"):
    ‘‘‘
    檢測img文件夾及文件夾中的內容、xlsx文件是否存在
    
    :param read_dir_name: 要讀取圖片的文件夾
    :param small_dir_name: 壓縮的圖片文件夾
    :param xlsx_file_name: excel文件名
    :return: 
            all_img:所有圖片對象
            xls_path:excel文件路徑
            img_path:圖片文件路徑
    ‘‘‘

    full_name = read_dir_name + "_img"
    img_path = os.path.join(os.getcwd(), full_name)

    LOGGER.info(img_path)
    assert os.path.isdir(img_path), "what?你都沒有{}文件夾,我咋給你貼圖啊!!!".format(full_name)

    all_img = os.listdir(img_path)
    assert all_img, "{}文件夾裏啥也沒有,咋貼!!!".format(full_name)

    xls_path = os.path.join(os.getcwd(), xlsx_file_name)
    assert os.path.isfile(xls_path), "{}文件不存在!!!".format(xlsx_file_name)

    # small_full_name = small_dir_name + datetime.datetime.now().strftime("%Y%m%d%H%M%S")
    if full_name == small_dir_name:
        return all_img, xls_path, img_path

    # os.mkdir("{}".format(small_full_name))
    # LOGGER.warning("新建文件夾{},壓縮後的圖片將存儲在該文件夾中。".format(small_dir_name))

    return all_img, xls_path, img_path


def _compression_img(read_dir_name, small_dir_name="small", height_shrink_multiple=2, width_shrink_multiple=2):
    ‘‘‘
    自動壓縮指定文件夾下的所有圖片

    :param read_dir_name: 讀取圖片文件夾的名稱
    :param small_dir_name:如果壓縮圖片就讀取該文件夾下的壓縮圖片
    :param height_shrink_multiple: 設置圖片壓縮高度的倍數
    :param width_shrink_multiple: 設置圖片壓縮寬度的倍數
    :return: small_dir_name: 壓縮後的圖片文件名
    ‘‘‘

    full_small_dir_name = small_dir_name + "_img"
    _check_os_dir(read_dir_name=read_dir_name, small_dir_name=full_small_dir_name)

    img_path = os.path.join(os.getcwd(), read_dir_name + "_img")
    all_img = os.listdir(img_path)
    for unit_img in all_img:
        try:
            img_num = unit_img.split("-")
            row = int(img_num[0])
            column = int(img_num[1].split(".")[0])
            suffix = (unit_img.split(".")[1])
            if column == 0:
                LOGGER.warning("圖片名稱格式有誤直接略過!錯誤文件名:{},“-”前後數字必須從1開始!".format(unit_img))
                continue
        except ValueError:
            LOGGER.warning("[-]圖片命名規則有誤直接略過!錯誤文件名是:{}".format(unit_img))
            continue

        img_fp = os.path.join(img_path, unit_img)
        origin_img = Image.open(img_fp)
        w, h = origin_img.size
        small_img = origin_img.resize((int(w / height_shrink_multiple), int(h / width_shrink_multiple)))

        small_img.save(os.path.join(os.getcwd(), "{}/{}-{}.{}".format(full_small_dir_name, row, column, suffix)))
        LOGGER.info(">> 正在處理圖像{}-{}.{},原像素高和寬{},壓縮後的高和寬{}".format(row, column, suffix, (w, h), small_img.size))
        try:
            small_img.close()
        except Exception as e:
            LOGGER.debug("未知錯誤\n{}".format(e))
    LOGGER.info("--------------圖片壓縮完成--------------")
    return small_dir_name


def write_img_for_xls(platform, read_dir_name, sheet_name="案例截圖", xlsx_file_name="test.xlsx"):
    """
    讀取同目錄img文件夾下的所有圖片,格式支持png\jpg\bmp。
    圖片必須遵從  1-1.png、1-2.png、2-1.png、2-2.png格式。
    註意:是將圖片寫入新的excel文件,如果老的excel中有數據,將會替換所有數據。

    platform: 平臺名稱,包括phone、pad,web目前沒實現
    read_dir_name: 要讀取圖片的文件夾名稱
    xlsx_file_name: 要寫入的xlsx文件名
    sheet_name: 寫入excel中sheet的名字

    :return: nothing
    """
    all_img, xls_path, img_path = _check_os_dir(xlsx_file_name=xlsx_file_name, read_dir_name=read_dir_name)

    w_book = xlsxwriter.Workbook(xls_path)
    img_sheet = w_book.add_worksheet(name=sheet_name)
    count_num = 0

    try:
        for unit_img in all_img:
            try:
                img_num = unit_img.split("-")
                row = int(img_num[0])
                column = int(img_num[1].split(".")[0])
                suffix = (unit_img.split(".")[1])
                if column == 0:
                    LOGGER.warning("圖片名稱格式有誤直接略過!錯誤文件名:{},“-”前後數字必須從1開始!".format(unit_img))
                    continue
            except ValueError:
                LOGGER.warning("[-]圖片命名規則有誤直接略過!錯誤文件名是:{}".format(unit_img))
                continue

            LOGGER.info(">> 正在貼圖到第{}條用例,第{}列...".format(row, column))
            img_sheet.set_column(firstcol=0, lastcol=0, width=8.38)
            img_sheet.write(row - 1, 0, "案例{}".format(row))

            small_img = os.path.join(os.getcwd(), "{}/{}-{}.{}".format(read_dir_name+"_img", row, column,
                                                                       suffix))

            img_sheet.set_column(firstcol=column, lastcol=column, width=PLATFORM.get(platform).get("width"))
            img_sheet.set_row(row=row - 1, height=PLATFORM.get(platform).get("height"))

            x_ = PLATFORM.get(platform).get("x_scale")
            y_ = PLATFORM.get(platform).get("y_scale")
            img_config = {"x_scale": x_, "y_scale": y_}

            result = img_sheet.insert_image(row - 1, column, small_img, img_config)
            img_sheet.write_url(row - 1, column + 1, url="https://my.oschina.net/medivhxu/blog/1590012")
            if not result:
                LOGGER.info("[+] 寫入成功!")
                count_num += 1
            else:
                LOGGER.error("[-] 寫入失敗!")

    except Exception as e:
        raise MyException("受到不明外力襲擊,程序...掛了....\n{}".format(e))
    finally:
        try:
            w_book.close()
        except PermissionError:
            raise MyException("你開著{}文件我讓我咋寫。。。趕緊關了!".format(xlsx_file_name))
    LOGGER.info("--------------貼圖完成--------------")
    LOGGER.info("程序貼圖數量:{},貼圖成功數量:{},貼圖失敗數量:{}".format(len(all_img), count_num, len(all_img) - count_num))


class MyException(Exception):
    pass


if __name__ == __main__:
    parser = argparse.ArgumentParser()
    parser.add_argument("-p", help="必須選擇平臺phone、pad、oppo")
    group = parser.add_mutually_exclusive_group()
    group.add_argument("-a", action="store_true", help="壓縮圖片且貼圖到excel")
    group.add_argument("-w", action="store_true", help="直接貼圖到excel")

    args = parser.parse_args()

    logging.basicConfig(level=logging.DEBUG,
                        format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s,
                        datefmt=%a, %d %b %Y %H:%M:%S,
                        filename=Paste_pictures_{}.log.format(datetime.datetime.now().strftime("%Y%m%d%H%M%S")),
                        filemode=w)

    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    formatter = logging.Formatter(%(name)-12s: %(levelname)-8s %(message)s)
    console.setFormatter(formatter)
    logging.getLogger(‘‘).addHandler(console)

    if args.p not in PLATFORM.keys():
        raise MyException("參數錯誤,必須在{}中選擇".format(PLATFORM.keys()))
    if args.a:
        LOGGER.info(">>> 選擇參數-a,即壓縮圖片,又貼圖。")
        r_small_dir_name = _compression_img(read_dir_name=args.p, small_dir_name="small")
        write_img_for_xls(platform=args.p, read_dir_name=r_small_dir_name)
    elif args.w:
        LOGGER.info(">>> 選擇參數-w,只貼圖。")
        write_img_for_xls(platform=args.p, read_dir_name=args.p)
    else:
        LOGGER.error("參數錯誤")

技術分享圖片技術分享圖片

windows command和linux/mac terminal下運行效果:

技術分享圖片

The end~

python+xlsxwriter+PIL自動壓圖貼圖到Excel小工具