Chromeのバージョンに合わせて最新-1のChromeDriverを取得する方法

## ChromeDriverを自動更新するのはなぜ？
- 自分のPCで画面操作の自動化をするにあたり、[UiPath](https://www.fsi.co.jp/rpa/uipath/)や[Power Automate](https://learn.microsoft.com/ja-jp/power-automate/desktop-flows/images)がある
- シンプルに実装する場合や画面のテスト実行時にSeleniumが使用されており、画面操作の自動化をプログラムとして実装する場合に利用することが多い
- 以前からSeleniumのChromeDriverを利用していたが、通常操作のChromeのバージョンに合わせて手作業でChromeDriverの更新が面倒
- システマティックに操作したいため、自動化を検討した

## 結果
- 自動化は可能そう
- 本日から稼働するため、挙動は今後検討しなければいけない可能性はある
- [ソースコード群はGithub上で展開](https://github.com/chibadai/get_selenium_chromedriver)していますので、良ければStarください
  - root/data/フォルダ配下に実際のデータが格納されるので、適宜調整してください
- 開発時間は2時間程度


## 必要な技術
- requests
- zipfile
- json

## 実装環境
- Windows 10
- Python 3.6.8(前から使っている古いバージョン)

## 実装内容について
### 参考ページ
- 不採用
  - 理由: 参考にはなったが、Chromeの自動化を実装するにあたり、なるべく原始的な処理で進めたかったため.
    - [Python で Selenium を使用し、ChromeDriver を利用している時、バージョンを自動でアップデートする方法](https://qiita.com/Colours/items/87489bc08878dee1c97d)
    - [How to get chrome version using command prompt in windows](https://stackoverflow.com/questions/50880917/how-to-get-chrome-version-using-command-prompt-in-windows)
    - [Chromium Docs](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/user_data_dir.md)
- **採用**
  - 理由: 直接Chromeからバージョンを取得できるため、コマンドプロンプトを使用するが必要経費として利用した
    - [コマンドプロンプトでdirにオプション付けた際のファイルへの出力方法について](https://answers.microsoft.com/ja-jp/windows/forum/all/%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%97/2487eefd-8dbb-4da1-841a-1ca9fb4e8605)

### 現在使用しているChromeのバージョンの取得
- Chromeのバージョンを直接取得するために、バッチで出力させた結果をテキストに出力することで後続処理に対応
```get_chrome_version.bat
dir /B /O-N "C:\Program Files\Google\Chrome\Application" | findstr ^[0-9].* > ".\..\data\chrome_version.txt"
```

### 公開しているChromeのバージョン(Json)を取得&保存
- ChromeのバージョンはJsonで提供されている
- 最新のデータを取得しJsonの結果を保存することで、後続で利用可能にする

```get-json.py
# 対象のURLからjsonデータを取得
json_name = "known-good-versions-with-downloads.json"
json_url = f"https://googlechromelabs.github.io/chrome-for-testing/{json_name}"
# json_url = "http://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json"
print(json_url)

# 上記のURLからrequestsでjsonデータを取得
import requests

# プロトコルはhttpsで、jsonデータを取得
response = requests.get(json_url)
print(response)

# jsonデータを取得
json_data = response.json()
# print(json_data)

import datetime

now_datetime = datetime.datetime.now()
print(now_datetime)
now_datetime_str = now_datetime.strftime("%Y%m%d-%H%M%S")

import os

save_dir_json_file_path = "./data/json"
os.makedirs(save_dir_json_file_path, exist_ok=True)

# jsonファイルを保存
save_json_file_path = f"{save_dir_json_file_path}/{now_datetime_str}-{json_name}"
import json

with open(save_json_file_path, "w") as f:
    json.dump(json_data, f, indent=4)

```

### Chromeのバージョンと、Chromeのバージョン管理Jsonの突合
- 現在利用しているChromeのバージョンとダウンロードしたJsonとを突合する
- 最新のChromeではなく、その1つ前のバージョンを取得する
- 取得したChromeのURLをChromeDriverに変換しダウンロード&展開する

```read-json.py
# Chrome Versionを取得しておく
# ChromeDriverのバージョンを取得する
with open("./data/chrome_version.txt", "r") as f:
    chrome_version = f.read()
print(chrome_version)
# chrome_version は最終行ので取得する
now_chrome_version = chrome_version.split("\n")[-2]
print(now_chrome_version)
split_now_chrome_version = [int(x) for x in now_chrome_version.split(".")]

## 保存したファイルを読み込み利用可能な状態にする
json_name = "known-good-versions-with-downloads.json"
# print(json_name)

json_dir_file_path = "./data/json"

# jsonファイル一覧を取得し、最新のjsonファイルを取得する
import glob
import os

json_files = glob.glob(f"{json_dir_file_path}/*.json")
latest_json_file_path = max(json_files, key=os.path.getctime)
print(latest_json_file_path)

# このデータを利用する
import json

with open(latest_json_file_path, "r") as f:
    json_data = json.load(f)
# print(json_data)
# print(type(json_data))
for first_key, value in json_data.items():
    # print(first_key, value)
    print(first_key)

key_timestamp = "timestamp"
# print(json_data[key_timestamp])
key_versions = "versions"
key_version = "version"
# print(json_data[key_versions])
# print(type(json_data[key_versions]))
json_data_versions = json_data[key_versions]
chrome_driver_version_dict = {}
for json_data_idx in range(0, len(json_data_versions)):
    json_data_version = json_data_versions[json_data_idx]
    chrome_driver_version_dict[json_data_idx] = {}
    # print(json_data_version)
    # print(type(json_data_version))
    """json_data_version layout
    version 113.0.5672.0
    revision 1121455
    downloads {'chrome': [{'platform': 'linux64', 'url': 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/linux64/chrome-linux64.zip'}, {'platform': 'mac-arm64', 'url': 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/mac-arm64/chrome-mac-arm64.zip'}, {'platform': 'mac-x64', 'url': 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/mac-x64/chrome-mac-x64.zip'}, {'platform': 'win32', 'url': 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/win32/chrome-win32.zip'}, {'platform': 'win64', 'url': 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/113.0.5672.0/win64/chrome-win64.zip'}]}
    """
    # for key_seconds, value in json_data_version.items():
    #     print(key_seconds, value)
    # key_version = "version"
    key_revision = "revision"
    key_downloads = "downloads"
    json_data_version_version = json_data_version[key_version]
    # print(json_data_version_version)
    json_data_version_revision = json_data_version[key_revision]
    # print(json_data_version_revision)
    json_data_version_downloads = json_data_version[key_downloads]
    # print(json_data_version_downloads)
    json_data_version_downloads_chrome = json_data_version_downloads["chrome"]
    # print(type(json_data_version_downloads_chrome))
    # print(json_data_version_downloads_chrome)
    target_os_type = "win64"
    key_platform = "platform"
    for dl_chrome_idx in range(0, len(json_data_version_downloads_chrome)):
        json_data_version_downloads_chrome_dict = json_data_version_downloads_chrome[
            dl_chrome_idx
        ]
        # print(json_data_version_downloads_chrome_dict)
        if json_data_version_downloads_chrome_dict[key_platform] == target_os_type:
            # print(json_data_version_downloads_chrome_dict)
            break
    # break

    chrome_driver_version_dict[json_data_idx][key_version] = json_data_version_version
    chrome_driver_version_dict[json_data_idx][key_revision] = json_data_version_revision
    # chrome_driver_version_dict[json_data_idx][key_downloads] = json_data_version_downloads
    chrome_driver_version_dict[json_data_idx][
        key_platform
    ] = json_data_version_downloads_chrome_dict["url"]
# print(chrome_driver_version_dict)
for json_data_idx in chrome_driver_version_dict.keys():
    # print(json_data_idx, chrome_driver_version_dict[json_data_idx])
    version = chrome_driver_version_dict[json_data_idx][key_version]
    split_version = [int(x) for x in version.split(".")]
    # print(split_version, split_now_chrome_version)
    if split_version[0] == split_now_chrome_version[0]:
        # print(split_version, split_now_chrome_version)
        if split_version[1] == split_now_chrome_version[1]:
            # print(split_version, split_now_chrome_version)
            if split_version[2] == split_now_chrome_version[2]:
                print(split_version, split_now_chrome_version)
                if split_version[3] >= split_now_chrome_version[3]:
                    print(split_version[3], split_now_chrome_version[3])
                    print("OK")
                    break
    if split_version[0] > split_now_chrome_version[0]:
        break
    # if version == now_chrome_version:
    #     print("OK")
    #     break

# print(chrome_driver_version_dict[json_data_idx][key_version])
print(chrome_driver_version_dict[json_data_idx - 1][key_version])
print(chrome_driver_version_dict[json_data_idx - 1])

# 上記のデータをダウンロードする
chrome_download_url = chrome_driver_version_dict[json_data_idx - 1][key_platform]
split_chrome_download_url = chrome_download_url.split("/")
chrome_driver_download_url = (
    "/".join(split_chrome_download_url[:-1])
    + "/"
    + split_chrome_download_url[-1].replace("chrome", "chromedriver")
)
# https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/120.0.6099.109/win64/chrome-win64.zip
# https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/120.0.6099.109/win64/chromedriver-win64.zip
print(chrome_driver_download_url)

import io
import zipfile
import requests

extract_dir = ".\data"

response = requests.get(chrome_driver_download_url)
bytes_io = io.BytesIO(response.content)

with zipfile.ZipFile(bytes_io) as zip:
    zip.extractall(extract_dir)

```

## 終わりに
- シンプルな実装(リリースなどはしない)ものであれば、Copilotと一緒に開発すればハードルはだいぶ下がったのが良かった
- もちろん、Copilotの実装で動かない場合もあるから注意は必要
- イメージがあれば、結構な開発速度になっていそう
- ChromeDriverのアップデートについては記事が少なかったのが意外だった
- また何かあれば記事を書いていく予定
  - 興味がある人がいればいいねや「XXXの実装をしてほしい」などをコメントに記載いただければ検討します