🚗

【C#】Chromeドライバを自動ダウンロードする

2023/08/08に公開

はじめに

突然Seleniumが動かなくなりました。
自動でドライバのダウンロードするようにしていたはずなんですが、いったいなぜ。

Chromeドライバのダウンロードページをのぞいてみると、以下の記述が。
https://chromedriver.chromium.org/downloads

以下、翻訳です。

バージョン 115 以降、Chromeドライバの提供方法が「Chrome for Testing」という方法に変更したようです。
Chrome本体とChromeドライバがセットでバージョン管理されるようになりました。また、JSON API エンドポイントから各バージョンのファイルURLも取得できるようになっています。

今までダウンロードしていたスクリプトが使用できなくなったため、JSON API エンドポイントを使用してChromeドライバを取得してみます。

概要

今回作成したプログラムの流れは以下の通りです。

  1. レジストリからChromeのメジャーバージョンを取得
  2. JSON APIエンドポイントからChromeドライバのURLを取得
  3. Chromeドライバをダウンロードする
  4. ダウンロードファイルを解凍する

やってみた

前提としてWindows環境で作成しています。

プロジェクトの作成

Visual Studioから以下の手順でプロジェクトを作成しました。
「新しいプロジェクトの作成」を選択
 ⇒ 「コンソールアプリ」を選択
 ⇒ プロジェクト名を入力
 ⇒ フレームワークに「.Net 6.0」を選択 「最上位レベルのステートメントを使用しない」にチェック

プログラム

プログラムの詳しい説明については、コメントを参照してください。
なお、エラー処理などについてはお好みで調整してください。

using Microsoft.Win32;
using System.IO.Compression;
using System.Net.Http.Json;
using System.Runtime.Versioning;

namespace ChromeDriverDownload
{
    internal class Program
    {
        // APIからのレスポンス定義
        class ChromeJsonApi
        {
            public Dictionary<string, Milestone> Milestones { get; set; } = new();
        }

        class Milestone
        {
            public Dictionary<string, List<DownLoad>> Downloads { get; set; } = new();
        }

        class DownLoad
        {
            public string Platform { get; set; } = string.Empty;
            public string Url { get; set; } = string.Empty;
        }

        [SupportedOSPlatform("windows")]
        static async Task Main()
        {
            var chromeVer = GetChromeMajorVer();
            if (string.IsNullOrEmpty(chromeVer))
            {
                Console.WriteLine("Chromeバージョンの取得に失敗しました。");
                return;
            }

            var url = await GetChromeDriverUrl(chromeVer, "win64");
            if (string.IsNullOrEmpty(url))
            {
                Console.WriteLine("ChromeドライバのURL取得に失敗しました。");
                return;
            }

            var downloadPath = @"C:\temp";

            var downloadZipPath = await DownloadDriver(url, downloadPath);
            if (string.IsNullOrEmpty(downloadZipPath))
            {
                Console.WriteLine("Chromeドライバのダウンロードに失敗しました。");
                return;
            }

            ZipFile.ExtractToDirectory(downloadZipPath, downloadPath, true);
        }

        [SupportedOSPlatform("windows")]
        // 自身のマシンのChromeメジャーバージョンを取得する
        private static string? GetChromeMajorVer()
        {
            // レジストリを開く
            // 「コンピューター\HKEY_CURRENT_USER\SOFTWARE\Google\Chrome\BLBeacon」
            using var regkey = Registry.CurrentUser.OpenSubKey(
                @"SOFTWARE\Google\Chrome\BLBeacon");
            if (regkey is null) return null;

            // レジストリの値を取得
            var chromeVer = regkey.GetValue("version") as string;

            // 取得失敗
            if (string.IsNullOrEmpty(chromeVer)) return null;

            Console.WriteLine($"ChromeVer:{chromeVer}"); // 115.0.5790.111 など

            // メジャーバージョンを取得
            return chromeVer.Split('.')[0];
        }

        // ChromeドライバのURLを取得する
        private static async Task<string?> GetChromeDriverUrl(
            string chromeVer, string platform)
        {
            HttpClient client = new();

            // JSON API endpointsを参照
            var res = await client.GetAsync(
                @"https://googlechromelabs.github.io/chrome-for-testing/latest-versions-per-milestone-with-downloads.json");

            // APIアクセス失敗
            if (!res.IsSuccessStatusCode) return null;

            var json = await res.Content.ReadFromJsonAsync<ChromeJsonApi>();
            if (json is null) return null;

            // 対象のChromeバージョンを見つける
            json.Milestones.TryGetValue(chromeVer, out var milestone);
            if (milestone is null) return null;

            // 対象バージョンのChromeドライバを見つける
            milestone.Downloads.TryGetValue("chromedriver", out var download);
            if (download is null) return null;

            // 対象ドライバのプラットフォームを見つける
            var winDownload = download.FirstOrDefault(x => x.Platform == platform);
            if (winDownload is null) return null;

            Console.WriteLine($"chromeDriverUrl:{winDownload.Url}");

            return winDownload.Url;
        }

        // Chromeドライバをダウンロードする
        private static async Task<string?> DownloadDriver(
            string url, string downloadPath)
        {
            HttpClient client = new();

            var res = await client.GetAsync(url);
            if (!res.IsSuccessStatusCode) return null;

            // ダウンロードファイルはZip
            var downloadZipPath = $@"{downloadPath}\chromedriver.zip";

            using var stream = await res.Content.ReadAsStreamAsync();
            using var outStream = File.Create(downloadZipPath);
            stream.CopyTo(outStream);

            return downloadZipPath;
        }
    }
}

まとめ

JSON APIエンドポイントはいくつか種類があるので、要件に合うものを選択してください。
https://github.com/GoogleChromeLabs/chrome-for-testing#json-api-endpoints

また突然のバージョンアップにより使えなくなるかもしれませんが、とりあえずは動くようになりました。そろそろPlaywrightで書き直さないといけないのかなぁ。
どなたかの参考になれば幸いです。

Discussion