5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Seleniumのchromedriver.exeとmsedgedriver.exeを自動更新する

Last updated at Posted at 2020-09-04

はじめに

ブラウザ自動テストのデファクトスタンダードであるSelenium
悩ましいことに操作対象のブラウザのバージョンに合わせてWebDriverを更新しなければいけません。

今回対象とするブラウザはGoogle ChromeChromium版Edgeです。
いずれも約4週間に一度のペースで自動更新されるため、「自動更新しよう!」ということになります。

chromedriverの更新方法と、msedgedriverの更新方法をまとめました。

2023/09月 v116 以降の chromedriver の公開URLが変更されたため、記事の内容を修正しました。

実装方針

それぞれのWebDriverのバージョン情報やバイナリを取得できるWebAPIが公開されています
それを組み合わせることで自動更新を実現します。
詳細はそれぞれ以下のパスを参照してください:

上記URLをみればわかる通り、やりたいことに合わせて色々アレンジできそうです。
※注意:以下、WindowsOS用のWin64バイナリを取得している例なので、適宜アレンジが必要です。

chromedriver.exe

  1. 操作対象となるchrome.exeのメジャーバージョンを取得する
    Windowsだったらこのパスにあるはず:C:\Program Files\Google\Chrome\Application\chrome.exe

  2. 以下のURIによって、メジャーバージョンをもとに最新のWebDriverのフルバージョンを取得する
    ※例えば v116 の場合:
    https://googlechromelabs.github.io/chrome-for-testing/LATEST_RELEASE_116
    ※注意)以下のURIは v115 以降は無効です:
    https://chromedriver.storage.googleapis.com/LATEST_RELEASE_114

  3. 以下のURIによって、WebDriverのフルバージョンをもとにzipファイルをダウンロードする
    ※例えば 116.0.5845.96 の場合:
    https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/116.0.5845.96/win64/chromedriver-win64.zip
    ※注意)以下のURIは v115 以降は無効です:
    https://chromedriver.storage.googleapis.com/114.0.5735.90/chromedriver_win32.zip

  4. ダウンロードしたzipファイルを展開する

msedgedriver.exe

  1. 操作対象となるmsedge.exeのメジャーバージョンを取得する
    Windowsだったらこのパスにあるはず:C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe

  2. 以下のURIによって、メジャーバージョンをもとに最新のWebDriverのフルバージョンを取得する
    ※例えば v116 の場合:
    https://msedgewebdriverstorage.blob.core.windows.net/edgewebdriver/LATEST_RELEASE_116

  3. 以下のURIによって、WebDriverのフルバージョンをもとにzipファイルをダウンロードする
    ※例えば 116.0.1938.69 の場合:
    https://msedgewebdriverstorage.blob.core.windows.net/edgewebdriver/116.0.1938.69/edgedriver_win64.zip

  4. ダウンロードしたzipファイルを展開する

実装例(C#)

C#で実装するとしたらこんな感じです。
注意:msedgedriverのバージョン取得結果の末尾に改行が入ってしまうのでトリミング処理が必要です。

using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

namespace ConsoleApp
{
    internal class Program
    {
        private static async Task Main(string[] args)
        {
            await UpdateChromeDriverAsync();
            await UpdateMsEdgeDriver();
        }

        public static async Task UpdateChromeDriverAsync()
        {
            // exeを配置したいフォルダを指定
            var directory = "chromedriver_" + Guid.NewGuid().ToString();

            var path = @"C:\Program Files\Google\Chrome\Application\chrome.exe";
            var majorversion = FileVersionInfo.GetVersionInfo(path).FileMajorPart;

            var versionUri = new Uri($@"https://googlechromelabs.github.io/chrome-for-testing/LATEST_RELEASE_{majorversion}");
            var version = await GetResponseAsStringAsync(versionUri);

            var driverUri = new Uri($@"https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/{version}/win64/chromedriver-win64.zip");
            await DownloadAsync(driverUri, directory);
        }

        public static async Task UpdateMsEdgeDriver()
        {
            // exeを配置したいフォルダを指定
            var directory = "msedgedriver_" + Guid.NewGuid().ToString();

            var path = @"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe";
            var majorversion = FileVersionInfo.GetVersionInfo(path).FileMajorPart;

            var versionUri = new Uri($@"https://msedgewebdriverstorage.blob.core.windows.net/edgewebdriver/LATEST_RELEASE_{majorversion}");
            var version = await GetResponseAsStringAsync(versionUri);

            // 末尾に改行コードが入ってしまうので削除する
            version = version.Trim();

            var driverUri = new Uri($@"https://msedgewebdriverstorage.blob.core.windows.net/edgewebdriver/{version}/edgedriver_win64.zip");
            await DownloadAsync(driverUri, directory);
        }

        private static async Task<string> GetResponseAsStringAsync(Uri uri)
        {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
            using (var httpClient = new HttpClient())
            using (var request = new HttpRequestMessage(HttpMethod.Get, uri))
            using (var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false))
            {
                using (var content = response.Content)
                {
                    return await content.ReadAsStringAsync();
                }
            }
        }

        private static async Task DownloadAsync(Uri uri, string extractDirectory)
        {
            if (!Directory.Exists(extractDirectory))
            {
                _ = Directory.CreateDirectory(extractDirectory);
            }

            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
            using (var httpClient = new HttpClient())
            using (var request = new HttpRequestMessage(HttpMethod.Get, uri))
            using (var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false))
            using (var content = response.Content)
            using (var stream = await content.ReadAsStreamAsync())
            using (var archive = new ZipArchive(stream))
            {
                UnZip(extractDirectory, archive);
            }
        }

        private static void UnZip(string extractDirectory, ZipArchive archive)
        {
            foreach (var entry in archive.Entries.Where(x => Path.GetExtension(x.Name) == ".exe"))
            {
                var path = Path.GetFullPath(Path.Combine(extractDirectory, Path.GetFileName(entry.FullName)));
                using (var stream = entry.Open())
                using (var writer = new FileStream(path, FileMode.Create, FileAccess.Write))
                {
                    var buffer = new byte[2048];
                    int len;
                    while ((len = stream.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        writer.Write(buffer, 0, len);
                    }
                }
            }
        }
    }
}

ブラウザパスの取得について

ブラウザがインストールされいているパスが環境によって異なることがあります。
そのため、更新ロジックのバージョン取得の部分が適切に動作しないことがあります。

その対策として、PATH でブラウザ指定してヘッドレスモードで一度起動し、
そのプロセスからファイルパスを取得する方法を提示します。
私はこの手法で driver を安定して更新することができています。

public static class BrowserFile
{
    public static string GetChromePath()
    {
        return GetPath("chrome");
    }
    public static string GetMsedgePath()
    {
        return GetPath("msedge");
    }

    private static string GetPath(string cmd)
    {
        try
        {
            string path;
            using (var p = Process.Start(cmd, "--headless"))
            {
                _ = p.WaitForInputIdle();
                path = p.MainModule.FileName;
                p.Kill();
            }

            return path;
        }
        catch
        {
            return null;
        }
    }
}

おわりに

同じ目的の記事がたくさんありましたが、
本質的なところを端的にまとめている記事がなかったので、本質的なところを端的にまとめてみました。
また、今後需要が伸びてくるであろうmsedgedriverにも言及し、競合記事との差別化を図りました。

5
7
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?