Web画面のエビデンス取得の自動化

エビデンス取得が楽になるプログラムをpythonで書いてみた。ほとんど参考ページのまんまですが。。。
# はじめに
画面テストは以下の作業がある。2,3,4を2クリックで自動化してみる。
1.対象の画面まで遷移
2.画面キャプチャ取得(windows + shift + s -> 対象範囲をドラッグ)
3.Excelのシートに貼り付け(Excelに切り替えて -> ctrl + v)
4.画像サイズを適当なサイズに縮小(画像選択　-> ドラッグで縮小)


# 検討内容
[1.スクリーンショットを撮る範囲を指定する](#1-スクリーンショットを撮る範囲を指定する)
[2.スクリーンショットを取得する](#2-スクリーンショットを取得する)
[3.完成プログラム](#3-完成プログラム)
[4.おまけ(応用編)](#4-おまけ)
[5.最近は・・・](#5-最近は・・・)

# 1. スクリーンショットを撮る範囲を指定する

```python
    リンクページの以下の部分だけ変更してみた。
    # 範囲の確認
    print(f"{left_up_x} {left_up_y} {right_down_x-left_up_x} {right_down_y-left_up_y}")  
```
```
出力結果：242 260 957 748
```
# 2. スクリーンショットを取得する
```Java
座標はクリックイベントで取得せず引数から取得するように変更した
def main(args):
    with mouse.Listener(on_click=on_click) as listener:
        listener.join()
    # スクリーンショットを保存
    pyautogui.screenshot("screenshot.png", region=(args.left_up_x, args.left_up_y, args.right_down_x, args.right_down_y)) 
    
on_clickはウインドウ切り替えのための１回と画面選択をするための１回に変更した。
def on_click(x, y, button, pressed):
    global num_press
    if pressed:
        # 1回目のクリックはウィンドウの切り替え
        if num_press == 0:
            num_press = 1
        # 2回目のクリックは終了
        else:
            return False

```

▼ 実行結果
スクリーンショットがpythonプログラムと同じフォルダに保存されています。
![evidence.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324903/dba529ea-fd7c-fbd4-d1e6-2fcb598a1faa.png)

▼ 画像をエクセルに貼り付ける
openpyxlモジュールを使ってExcelを操作する。
スクリーンショット画像を読み込んで、Excelをオープンし、シート選択し、指定セルに貼り付ける。

# 3 完成プログラム
短いプログラムで仕事効率を上げます！
```
# evidence.py
import os
import argparse
from pynput import mouse
import pyautogui
import win32clipboard
import io
import sys
import openpyxl
from openpyxl.drawing.image import Image
from openpyxl.utils import get_column_letter

# //////////////////////////////////////////////////
# プログラムの説明
#   ブラウザ画面の指定範囲をスクリーンショットする
#   スクリーンショットをエクセルに貼り付ける
# 実行準備
#   Excelファイルは閉じておく
#   実行ファイルのあるフォルダでpowershellを開く
#   実行ファイル名と引数を入力してEnter
# 引数の確認方法
#   実行ファイル名 -h
# 実行例
#   python .\evidence.py 242 260 957 748 Book1.xlsx Sheet11 B3
# 実行後の画面操作方法
#   1回目のクリックでブラウザを選択
#   2回目のクリックでエビデンスの画面を選択
# 結果確認方法
# 　Excelファイルを開いて画像が貼られていることを確認
# //////////////////////////////////////////////////

# 初期値
num_press = 0
last_height_size = 480

def main(args):
    with mouse.Listener(on_click=on_click) as listener:
        listener.join()
    # スクリーンショットを保存
    pyautogui.screenshot("screenshot.png", region=(args.left_up_x, args.left_up_y, args.right_down_x, args.right_down_y)) 
    copy_paste(args)

def copy_paste(args): 
    # スクリーンショット画像を取得
    img = Image(os.path.join(".\\","screenshot.png"))
    # 画像サイズ縮小
    width_size = img.width
    height_size = img.height
    zoom_percentage = last_height_size / height_size
    img.width = width_size * zoom_percentage
    img.height = last_height_size
    #すでに存在するExcelワークブックを読み込む 
    wb = openpyxl.load_workbook(args.book_name, read_only=False)
    #ワークブック内のアクティブなシートを取得
    ws = wb.active
    # 画像貼り付け
    ws.add_image(img, f"{args.select_range}")
    # 保存
    wb.save(args.book_name)
   
def on_click(x, y, button, pressed):
    global num_press
    if pressed:
        # 1回目のクリックはウィンドウの切り替え
        if num_press == 0:
            num_press = 1
        # 2回目のクリックは終了
        else:
            return False

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("left_up_x", help="X軸始点", type=int)
    parser.add_argument("left_up_y", help="Y軸始点", type=int)
    parser.add_argument("right_down_x", help="X軸終点", type=int)
    parser.add_argument("right_down_y", help="Y軸終点", type=int)
    parser.add_argument("book_name", help="使用する既存のExcelファイル名", type=str)
    parser.add_argument("sheet_name", help="使用する既存のシート名", type=str)
    parser.add_argument("select_range", help="貼り付けるセル", type=str)
    args = parser.parse_args()
    main(args)
```
▼ 以下の引数で実行
```
python .\evidence.py 242 260 957 748 test.xlsx test1 B3
```
▼ 実行結果
引数で指定したB3に貼り付けられています。
![2023-11-21 204410.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324903/24caba70-6889-1baa-d243-366c9d899407.png)

# 4 おまけ
一回の実行で繰り返しエビデンスを取得します。
* [毎回貼り付け位置を指定編](#-毎回貼り付け位置を指定編)
* [貼り付け位置を自動計算縦編](#-貼り付け位置を自動計算縦編)
* [貼り付け位置を自動計算Z編](#-貼り付け位置を自動計算Z編)

# 毎回貼り付け位置を指定編
```
# getEvidenceRepeatManual.py
import os
import argparse
from pynput import mouse
import pyautogui
import win32clipboard
import io
import sys
import openpyxl
from openpyxl.drawing.image import Image

# //////////////////////////////////////////////////
# プログラムの説明
#   Excelの貼り付け位置を毎回指定してエビデンスを取得する
# 実行準備
#   Excelファイルは閉じておく
#   実行ファイルのあるフォルダでpowershellを開く
#   実行ファイル名と引数を入力してEnter
# 引数の確認方法
#   実行ファイル名 -h
# 実行例
#   python .\getEvidenceRepeat.py 242 260 957 748 Book1.xlsx Sheet11
# 実行後の画面操作方法
#   ①「続行する場合はExcelの貼り付け位置のRangeを入力してください。終了する場合はendを入力してください」
#      このメッセージが表示された時に取得対象の画面を開くことをお勧めする。
#   ②「対象画面を表示してクリックしてください」
　　　　このメッセージが表示された時にウィンドウを表示して画面をクリックする
#     ※操作は２クリックで行う必要がある。
#      1回目のクリックはウインドウ選択、2回目のクリックは画像選択
#   ③①~②を繰り返す。またはendを入力して終了する。
# 結果確認方法
# 　Excelファイルを開いて画像が貼られていることを確認
# Error
#   PermissionError: [Errno 13] Permission denied⇒Excelファイルが開いている
#   KeyError: "There is no item named '[Content_Types].xml' in the archive"⇒ファイルが壊れている
# //////////////////////////////////////////////////

# 定数
SCREENSHOT = "screenshot.png" # 一時画像ファイル名
# 初期値
num_press = 0
last_height_size = 480

def main(args):   
    input_val = input("続行する場合はExcelの貼り付け位置のRangeを入力してください。終了する場合はendを入力してください\n") 
    while True:
     if input_val == "end":
      break
     else:
      print("対象画面を表示してクリックしてください\n")
      with mouse.Listener(on_click=on_click) as listener:
        listener.join()
      screenshot(args, input_val)
      input_val = input("続行する場合はExcelのRangeを入力してください。終了する場合はendを入力してください\n")   
        
def screenshot(args, target_range):
    global num_press
    # スクリーンショットを保存
    pyautogui.screenshot(SCREENSHOT, region=(args.left_up_x, args.left_up_y, args.right_down_x, args.right_down_y)) 
    copy_paste(args, target_range)
    num_press = 0

def copy_paste(args, target_range): 
    # スクリーンショット画像を取得
    img = Image(os.path.join(".\\",SCREENSHOT))
    # 画像サイズ調整
    width_size = img.width
    height_size = img.height
    zoom_percentage = last_height_size / height_size
    img.width = width_size * zoom_percentage
    img.height = last_height_size
    #すでに存在するExcelワークブックを読み込む 
    wb = openpyxl.load_workbook(args.book_name, read_only=False)
    #ワークブック内のアクティブなシートを取得
    ws = wb.active
    # 画像貼り付け
    ws.add_image(img, f"{target_range}")
    # 保存
    wb.save(args.book_name)

def on_click(x, y, button, pressed):
    global num_press
    if pressed:
        # 1回目のクリックはウィンドウの切り替え
        if num_press == 0:
            num_press = 1
        # 2回目のクリックは終了
        else:
            return False

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("left_up_x", help="X軸始点", type=int)
    parser.add_argument("left_up_y", help="Y軸始点", type=int)
    parser.add_argument("right_down_x", help="X軸終点", type=int)
    parser.add_argument("right_down_y", help="Y軸終点", type=int)
    parser.add_argument("book_name", help="使用する既存のExcelファイル名", type=str)
    parser.add_argument("sheet_name", help="使用する既存のシート名", type=str)
    args = parser.parse_args()
    main(args)
```
▼ 実行
```
PS C:\Users\USER\Documents\python> python .\getEvidenceRepeatManual.py 242 260 957 748 test.xlsx test
続行する場合はExcelの貼り付け位置のRangeを入力してください。終了する場合はendを入力してください
B2
対象画面を表示してクリックしてください

続行する場合はExcelのRangeを入力してください。終了する場合はendを入力してください
S25
対象画面を表示してクリックしてください

続行する場合はExcelのRangeを入力してください。終了する場合はendを入力してください
end
```
▼ 結果
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324903/403b3dd0-fdc6-9d5e-a29a-a40156dc950a.png" width=50% height=50%>

# 貼り付け位置を自動計算 縦編
```
# getEvidenceRepeatZ.py
import os
import argparse
from pynput import mouse
import pyautogui
import win32clipboard
import io
import sys
import openpyxl
from openpyxl.drawing.image import Image
from openpyxl.utils import get_column_letter
import re

# //////////////////////////////////////////////////
# プログラムの説明
#   画面の指定範囲をスクリーンショットし、
#   引数で指定したRangeから自動計算して縦位置に貼り付けを繰り返し行う。
# 実行準備
#   貼り付けるシートのセルのサイズは縦36px,横36pxにする
#   Excelファイルは閉じておく
#   実行ファイルのあるフォルダでpowershellを開く
#   実行ファイル名と引数を入力してEnter
# 引数の確認方法
#   実行ファイル名 -h
# 実行例
#   python .\getEvidenceRepeat.py 242 260 957 748 Book1.xlsx Sheet11 B3
# 実行後の画面操作方法
#   ①「続行する場合はenterしてください。終了する場合はendを入力してください」
#      このメッセージが表示された時に取得対象の画面を開くことをお勧めする。
#   ②「対象画面を表示してクリックしてください」
　　　　このメッセージが表示された時にウィンドウを表示して画面をクリックする
#     ※操作は２クリックで行う必要がある。
#      1回目のクリックはウインドウ選択、2回目のクリックは画像選択
#   ③①~②を繰り返す。またはendを入力して終了する。
# 結果確認方法
# 　Excelファイルを開いて画像が貼られていることを確認
# Error
#   PermissionError: [Errno 13] Permission denied⇒Excelファイルが開いている
#   KeyError: "There is no item named '[Content_Types].xml' in the archive"⇒ファイルが壊れている
# //////////////////////////////////////////////////

# 定数
ROW_CALC = 24 # 画像サイズから行数に変換する値
COLUMN_CALC = 24 # 画像サイズから列数に変換する値
ROW_SPACE = 1 # 画像との間隔(縦)
COLUMN_SPACE = 1 # 画像との間隔(横)
SCREENSHOT = "screenshot.png" # 一時画像ファイル名
# 初期値
num_press = 0
last_height_size = 480
range = []
count = 0

def main(args):   
    global range, count
    input_val = input("続行する場合はenterしてください。終了する場合はendを入力してください\n") 
    while True:
     if input_val == "end":
      break
     else:
      count += 1
      print("対象画面を表示してクリックしてください\n")
      with mouse.Listener(on_click=on_click) as listener:
        listener.join()
      if len(range) == 0:
         range = split_cell(args.target_range)
      screenshot(args)      
      input_val = input("続行する場合はenterしてください。終了する場合はendを入力してください\n")   
        
def screenshot(args):
    global num_press, count
    # スクリーンショットを保存
    pyautogui.screenshot(SCREENSHOT, region=(args.left_up_x, args.left_up_y, args.right_down_x, args.right_down_y)) 
    copy_paste(args)
    num_press = 0

def copy_paste(args): 
    global range, count
    # スクリーンショット画像を取得
    img = Image(os.path.join(".\\",SCREENSHOT))
    # 画像サイズ調整
    width_size = img.width
    height_size = img.height
    zoom_percentage = last_height_size / height_size
    img.width = width_size * zoom_percentage
    img.height = last_height_size
    #すでに存在するExcelワークブックを読み込む 
    wb = openpyxl.load_workbook(args.book_name, read_only=False)
    #ワークブック内のアクティブなシートを取得
    ws = wb.active
    # 画像貼り付け
    ws.add_image(img, f"{get_column_letter(int(range[1]))}{range[0]}")
    # 保存
    wb.save(args.book_name)
    # 次のレンジを取得
    get_next_range(img)   

def on_click(x, y, button, pressed):
    global num_press
    if pressed:
        # 1回目のクリックはウィンドウの切り替え
        if num_press == 0:
            num_press = 1
        # 2回目のクリックは終了
        else:
            return False
        
def get_next_range(img):
    global range, count
    row_num = int(((img.height) // ROW_CALC + ROW_SPACE))
    col_num = int((img.width) // COLUMN_CALC + COLUMN_SPACE)
    if count % 2 == 0:
     # Vertical
     range[0] = str(row_num * int(count/ 2))
     range[1] = str(int(range[1]) - col_num)
    elif count % 2 == 1:
     # Horizontal
     range[1] = str(col_num + int(range[1]))

def split_cell(target_range):
    # rangeの列の文字列を取得
    column = re.search(r"[A-Z]+",target_range).group()
    # rangeの行の数字を取得
    row = target_range[re.search(r"[0-9]+", target_range).start():]
    # rangeの列の文字列から列番号を取得
    col = 0
    for i, val in enumerate(list(column)):
       col += pow(26, len(column) - i - 1) * (ord(val) - ord('A') + 1)
    return [row,col]

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("left_up_x", help="X軸始点", type=int)
    parser.add_argument("left_up_y", help="Y軸始点", type=int)
    parser.add_argument("right_down_x", help="X軸終点", type=int)
    parser.add_argument("right_down_y", help="Y軸終点", type=int)
    parser.add_argument("book_name", help="使用する既存のExcelファイル名", type=str)
    parser.add_argument("sheet_name", help="使用する既存のシート名", type=str)
    parser.add_argument("target_range", help="最初のセル", type=str)
    args = parser.parse_args()
    main(args)
```
▼ 実行
```
PS C:\Users\USER\Documents\python> python .\getEvidenceRepeatAutoI.py 242 260 957 748 test.xlsx test B2
続行する場合はenterしてください。終了する場合はendを入力してください

対象画面を表示してクリックしてください

続行する場合はenterしてください。終了する場合はendを入力してください

対象画面を表示してクリックしてください

続行する場合はenterしてください。終了する場合はendを入力してください
end
```
▼ 結果
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324903/6541e19f-57c6-0b99-6fc8-a32cb8dc108d.png" width=50% height=50%>

# 貼り付け位置を自動計算 Z編
```
# getEvidenceRepeatZ.py
import os
import argparse
from pynput import mouse
import pyautogui
import win32clipboard
import io
import sys
import openpyxl
from openpyxl.drawing.image import Image
from openpyxl.utils import get_column_letter
import re

# //////////////////////////////////////////////////
# プログラムの説明
#   画面の指定範囲をスクリーンショットし、
#   引数で指定したRangeから自動計算してレイアウトZで貼り付けを繰り返し行う。
# 実行準備
#   貼り付けるシートのセルのサイズは縦36px,横36pxにする
#   Excelファイルは閉じておく
#   実行ファイルのあるフォルダでpowershellを開く
#   実行ファイル名と引数を入力してEnter
# 引数の確認方法
#   実行ファイル名 -h
# 実行例
#   python .\getEvidenceRepeat.py 242 260 957 748 Book1.xlsx Sheet11 B3
# 実行後の画面操作方法
#   ①「続行する場合はenterしてください。終了する場合はendを入力してください」
#      このメッセージが表示された時に取得対象の画面を開くことをお勧めする。
#   ②「対象画面を表示してクリックしてください」
　　　　このメッセージが表示された時にウィンドウを表示して画面をクリックする
#     ※操作は２クリックで行う必要がある。
#      1回目のクリックはウインドウ選択、2回目のクリックは画像選択
#   ③①~②を繰り返す。またはendを入力して終了する。
# 結果確認方法
# 　Excelファイルを開いて画像が貼られていることを確認
# Error
#   PermissionError: [Errno 13] Permission denied⇒Excelファイルが開いている
#   KeyError: "There is no item named '[Content_Types].xml' in the archive"⇒ファイルが壊れている
# //////////////////////////////////////////////////

# 定数
ROW_CALC = 24 # 画像サイズから行数に変換する値
COLUMN_CALC = 24 # 画像サイズから列数に変換する値
ROW_SPACE = 1 # 画像との間隔(縦)
COLUMN_SPACE = 1 # 画像との間隔(横)
SCREENSHOT = "screenshot.png" # 一時画像ファイル名
# 初期値
num_press = 0
last_height_size = 480
range = []
count = 0

def main(args):   
    global range, count
    input_val = input("続行する場合はenterしてください。終了する場合はendを入力してください\n") 
    while True:
     if input_val == "end":
      break
     else:
      count += 1
      print("対象画面を表示してクリックしてください\n")
      with mouse.Listener(on_click=on_click) as listener:
        listener.join()
      if len(range) == 0:
         range = split_cell(args.target_range)
      screenshot(args)      
      input_val = input("続行する場合はenterしてください。終了する場合はendを入力してください\n")   
        
def screenshot(args):
    global num_press, count
    # スクリーンショットを保存
    pyautogui.screenshot(SCREENSHOT, region=(args.left_up_x, args.left_up_y, args.right_down_x, args.right_down_y)) 
    copy_paste(args)
    num_press = 0

def copy_paste(args): 
    global range, count
    # スクリーンショット画像を取得
    img = Image(os.path.join(".\\",SCREENSHOT))
    # 画像サイズ調整
    width_size = img.width
    height_size = img.height
    zoom_percentage = last_height_size / height_size
    img.width = width_size * zoom_percentage
    img.height = last_height_size
    #すでに存在するExcelワークブックを読み込む 
    wb = openpyxl.load_workbook(args.book_name, read_only=False)
    #ワークブック内のアクティブなシートを取得
    ws = wb.active
    # 画像貼り付け
    ws.add_image(img, f"{get_column_letter(int(range[1]))}{range[0]}")
    # 保存
    wb.save(args.book_name)
    # 次のレンジを取得
    get_next_range(img)   

def on_click(x, y, button, pressed):
    global num_press
    if pressed:
        # 1回目のクリックはウィンドウの切り替え
        if num_press == 0:
            num_press = 1
        # 2回目のクリックは終了
        else:
            return False
        
def get_next_range(img):
    global range, count
    row_num = int(((img.height) // ROW_CALC + ROW_SPACE))
    col_num = int((img.width) // COLUMN_CALC + COLUMN_SPACE)
    if count % 2 == 0:
     # Vertical
     range[0] = str(row_num * int(count/ 2))
     range[1] = str(int(range[1]) - col_num)
    elif count % 2 == 1:
     # Horizontal
     range[1] = str(col_num + int(range[1]))

def split_cell(target_range):
    # rangeの列の文字列を取得
    column = re.search(r"[A-Z]+",target_range).group()
    # rangeの行の数字を取得
    row = target_range[re.search(r"[0-9]+", target_range).start():]
    # rangeの列の文字列から列番号を取得
    col = 0
    for i, val in enumerate(list(column)):
       col += pow(26, len(column) - i - 1) * (ord(val) - ord('A') + 1)
    return [row,col]

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("left_up_x", help="X軸始点", type=int)
    parser.add_argument("left_up_y", help="Y軸始点", type=int)
    parser.add_argument("right_down_x", help="X軸終点", type=int)
    parser.add_argument("right_down_y", help="Y軸終点", type=int)
    parser.add_argument("book_name", help="使用する既存のExcelファイル名", type=str)
    parser.add_argument("sheet_name", help="使用する既存のシート名", type=str)
    parser.add_argument("target_range", help="最初のセル", type=str)
    args = parser.parse_args()
    main(args)
```
▼ 実行
```
PS C:\Users\USER\Documents\python> python .\getEvidenceRepeatAutoZ.py 242 260 957 748 test.xlsx test B2
続行する場合はenterしてください。終了する場合はendを入力してください

対象画面を表示してクリックしてください

続行する場合はenterしてください。終了する場合はendを入力してください

対象画面を表示してクリックしてください

続行する場合はenterしてください。終了する場合はendを入力してください

対象画面を表示してクリックしてください

続行する場合はenterしてください。終了する場合はendを入力してください
end
```
▼ 結果
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324903/1e6751d1-0b19-7976-198f-ab8bfa798205.png" width=50% height=50%>

#5-最近は・・・
エビデンスの取得ははCtrl+Cで順次コピーして行き、Windows+Vでコピーした画像を選択して貼り付けています。プログラム必要ありませんでした。

# おわりに
テストを終了してこんなのあったらよかったな。みたいな感じで作ってみました。
次のプロジェクトで役に立つかはわかりません。

# 参考リンク
* [Pythonで指定した範囲のスクリーンショットを撮る](https://qiita.com/kenshin-627/items/56b4d945ac63ad8c541c)
* [【Python】Excelへの画像挿入とサイズ調整の自動化](https://dev.classmethod.jp/articles/python-excel-image-resize/)
* [このプログラムの引数で使用した座標値取得の方法](https://qiita.com/flip-flop/items/efda285db6ead5a74d81)