概要
WebDriver for ChromeでRPAを利用していたら、ブラウザの自動更新が行われてしまった事で
エラーが発生してめんどくさかったのでダウンロード~更新までを自動化した。
Imports System.IO.Compression '参照設定 System.IO.Compression.FileSystem.dllとSystem.IO.Compression.dll
Imports Microsoft.Win32
Imports System.Net
Imports System.IO
''' <summary>
''' chromeWebDriverが古い場合はダウンロードして置き換える
''' </summary>
''' <remarks></remarks>
Public Sub updateChomeDriver()
Dim zipPath As String = "C:\chrome_driver\chromedriver_win32.zip"
Dim extractPath As String = "C:\chrome_driver"
Dim strFileDrvVersion As String = ""
If Not Directory.Exists(extractPath) Then
Directory.CreateDirectory(extractPath)
End If
'現在インストールされているChromeブラウザバージョン取得
Dim strChromeVer As String = GetChromeVersion()
'現在インストールされているChromeWebDriverバージョン取得
If IO.File.Exists(extractPath & "\chromedriver.exe") Then
strFileDrvVersion = Runcmd("C:\chrome_driver\chromedriver.exe", "-v")
Else
strFileDrvVersion = ""
End If
'現在のChromeバージョンに見合ったDriverバージョンをサイトから調べる
Dim siteVerURL As String = ""
Using client As WebClient = New WebClient()
siteVerURL = client.DownloadString("https://chromedriver.storage.googleapis.com/LATEST_RELEASE_" + strChromeVer.Substring(0, strChromeVer.LastIndexOf(".")))
End Using
'現在のDriverバージョンとChromeブラウザバージョンを比較する
If strFileDrvVersion = siteVerURL Then
'同じならば処理しない
Exit Sub
End If
'カレントディレクトリ設定
Directory.SetCurrentDirectory("C:\chrome_driver")
'ブラウザに合ったDriverバージョンをダウンロードする
Using wc As New WebClient
wc.DownloadFile("https://chromedriver.storage.googleapis.com/" + siteVerURL + "/chromedriver_win32.zip", "chromedriver_win32.zip")
End Using
'ZIP書庫を開く
Using z As ZipArchive = ZipFile.OpenRead(zipPath)
Dim za As ZipArchiveEntry = z.GetEntry("chromedriver.exe")
If za Is Nothing Then
'見つからなかった時
Console.WriteLine("chromedriver_win32/chromedriver.exe が見つかりませんでした。")
Else
'2番目の引数をTrueにすると、上書きする
za.ExtractToFile("C:\chrome_driver\chromedriver.exe", True)
retMsg = "ChromeDriverを" & siteVerURL & "に更新しました。"
End If
End Using
End Sub
''' <summary>
''' インストールされているChromeブラウザのバージョンを取得する
''' </summary>
''' <returns>バージョン文字列</returns>
''' <remarks></remarks>
Private Function GetChromeVersion() As String
Dim wowNode As String = String.Empty
If Environment.Is64BitOperatingSystem Then wowNode = "Wow6432Node\"
Dim regKey As RegistryKey = Registry.LocalMachine
Dim keyPath As RegistryKey = regKey.OpenSubKey("Software\" + wowNode + "Google\Update\Clients")
If IsDBNull(keyPath) Then
regKey = Registry.CurrentUser
keyPath = regKey.OpenSubKey("Software\" + wowNode + "Google\Update\Clients")
End If
If IsDBNull(keyPath) Then
regKey = Registry.LocalMachine
keyPath = regKey.OpenSubKey("Software\Google\Update\Clients")
End If
If IsDBNull(keyPath) Then
regKey = Registry.CurrentUser
keyPath = regKey.OpenSubKey("Software\Google\Update\Clients")
End If
If Not IsDBNull(keyPath) Then
Dim subKeys As String() = keyPath.GetSubKeyNames()
For Each subKey As String In subKeys
Dim value = keyPath.OpenSubKey(subKey).GetValue("name")
Dim found = False
If Not IsDBNull(value) Then
found = value.ToString().Equals("Google Chrome", StringComparison.InvariantCultureIgnoreCase)
End If
If found Then
Return keyPath.OpenSubKey(subKey).GetValue("pv").ToString()
End If
Next
End If
Return "NotFound"
End Function
''' <summary>
''' コマンドプロンプトでChromeDriverのバージョンを取得する
''' </summary>
''' <param name="chromeDriverPath"></param>
''' <param name="strcmd"></param>
''' <returns></returns>
''' <remarks></remarks>
Function Runcmd(chromeDriverPath As String, strcmd As String) As String
'Processオブジェクト
Dim proc As New System.Diagnostics.Process()
With proc.StartInfo
.FileName = chromeDriverPath
'出力読取り可能化
.UseShellExecute = False
.RedirectStandardOutput = True
.RedirectStandardInput = False
'コマンドプロンプトのウィンドウ非表示
.CreateNoWindow = True
'コマンド(/c:コマンド実行後、コマンドを実行したcmd.exeを終了)
.Arguments = "/c " & strcmd
End With
'コマンド実行
proc.Start()
'実行結果取得
Dim result As String = proc.StandardOutput.ReadToEnd()
proc.WaitForExit()
proc.Close()
Dim res As String() = result.Split(" ")
Return res(1)
End Function
Chromeブラウザの自動アップデート停止
ChromeDriverのLATEST_RELEASEが記載されているサイトの更新が行われなくなったので、
Chromeブラウザの自動アップデートを停止して手動対応する事にした。
【手順】
①GoogleUpdateAdmx管理用テンプレートをダウンロードする。
https://dl.google.com/update2/enterprise/googleupdateadmx.zip
②C:\Windows\PolicyDefinitionsへコピーする
解凍したgoogle.adml ファイルと GoogleUpdate.adml ファイルをC:\Windows\PolicyDefinitionsルートへコピー。
解凍したen-USフォルダの中身をC:\Windows\PolicyDefinitions\en-USフォルダへコピーする。
③コマンドプロンプトでgpedit.mscを起動する。
グループ ポリシー([コンピューターの構成] フォルダ)で次のように操作する。
[Google] ⇒ [Google Update] ⇒ [Applications] ⇒[GoogleChrome] に移動する。
[Update policy override] をオンにする。
[Options] で [Disable updates] にする。
④動作チェック
ブラウザのヘルプの[Chrome について] をクリックして、
「管理者が更新を無効にした」旨のメッセージが出ていれば効いている。
上述の手順で自動アップデートが行われなくなるので、
タイミングを見計らって手動でブラウザ更新とchromedriver.exe更新を行います。
chromeDriverの最新exeは以下のサイトに掲載されています。
https://googlechromelabs.github.io/chrome-for-testing/#stable
また、chromeブラウザの向こう3バージョンのリリースロードマップが
以下のサイトに掲載されています。
https://chromestatus.com/roadmap
その後
すっかりほったらかしになっていた本件だが、ChromeDriverのダウンロードURLが取得できるAPIが公開されていたので再度自動化にトライしてみた。
とりあえず以下のChromeDriverFinder.vbをプロジェクトに追加する。
Imports System.IO
Imports System.Net.Http
Imports Newtonsoft.Json
' JSONのルートオブジェクトに対応
Public Class ChromeVersionsInfo
<JsonProperty("timestamp")>
Public Property Timestamp As String
<JsonProperty("versions")>
Public Property Versions As List(Of VersionDetail)
End Class
' 各バージョンの詳細情報
Public Class VersionDetail
<JsonProperty("version")>
Public Property Version As String
<JsonProperty("revision")>
Public Property Revision As String
<JsonProperty("downloads")>
Public Property Downloads As Downloads
End Class
' ダウンロード可能なファイルのカテゴリ
Public Class Downloads
<JsonProperty("chrome")>
Public Property Chrome As List(Of DownloadItem)
<JsonProperty("chromedriver")>
Public Property ChromeDriver As List(Of DownloadItem)
End Class
' 各ダウンロードファイルの情報
Public Class DownloadItem
<JsonProperty("platform")>
Public Property Platform As String
<JsonProperty("url")>
Public Property Url As String
End Class
Public Class ChromeDriverFinder
Private Const ApiEndpoint As String = "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json"
''' <summary>
''' 指定されたChromeバージョンに一致、または最も近い過去のバージョンのChromeDriverのダウンロードURLを取得します。
''' </summary>
''' <param name="targetVersionString">検索するChromeのバージョン (例: "142.0.7444.163")</param>
''' <param name="targetPlatform">プラットフォーム (例: "win64", "win32", "mac-x64", "mac-arm64", "linux64")</param>
''' <returns>ダウンロードURL。見つからない場合は空の文字列を返します。</returns>
Public Shared Async Function GetChromeDriverUrlAsync(targetVersionString As String, targetPlatform As String) As Task(Of String)
Try
Using client As New HttpClient()
Dim jsonString As String = Await client.GetStringAsync(ApiEndpoint)
Dim chromeInfo As ChromeVersionsInfo = JsonConvert.DeserializeObject(Of ChromeVersionsInfo)(jsonString)
Dim targetVersionDetail As VersionDetail = Nothing
' 1. まずは完全一致を探す
targetVersionDetail = chromeInfo.Versions.FirstOrDefault(Function(v) v.Version = targetVersionString)
' 2. 完全一致がない場合、最も近い過去のバージョンを探す
If targetVersionDetail Is Nothing Then
Dim targetVersion As Version = Nothing
' 入力されたバージョン文字列をVersionオブジェクトに変換
If Version.TryParse(targetVersionString, targetVersion) Then
' 条件に合うバージョンを絞り込む
' - メジャーバージョンが同じ
' - 指定バージョン以下である
' - その中で降順に並び替え、最初のもの(=最も近い)を取得
targetVersionDetail = chromeInfo.Versions.
Select(Function(v)
' リスト内の各バージョンもVersionオブジェクトに変換しておく
Dim currentVersion As Version = Nothing
Version.TryParse(v.Version, currentVersion)
Return New With {Key .Detail = v, Key .VersionObject = currentVersion}
End Function).
Where(Function(x) x.VersionObject IsNot Nothing AndAlso
x.VersionObject.Major = targetVersion.Major AndAlso
x.VersionObject <= targetVersion).
OrderByDescending(Function(x) x.VersionObject).
Select(Function(x) x.Detail).
FirstOrDefault()
End If
End If
' 3. 見つかったバージョンの詳細からURLを取得
If targetVersionDetail IsNot Nothing Then
Dim chromedriverDownload As DownloadItem = targetVersionDetail.Downloads.ChromeDriver.FirstOrDefault(Function(d) d.Platform = targetPlatform)
If chromedriverDownload IsNot Nothing Then
' 見つかったバージョン番号も一緒に返すようにすると、どのバージョンが選択されたか分かりやすい
' Console.WriteLine($"選択されたバージョン: {targetVersionDetail.Version}")
Return chromedriverDownload.Url
End If
End If
End Using
Catch ex As Exception
Console.WriteLine($"エラーが発生しました: {ex.Message}")
End Try
Return String.Empty
End Function
''' <summary>
''' PCにインストールされているGoogle Chromeのバージョンを取得します。
''' </summary>
''' <returns>バージョンの文字列 (例: "125.0.6422.112")。見つからない場合は空の文字列を返します。</returns>
Public Shared Function GetInstalledChromeVersion(chromePath As String) As String
' 1. レジストリなどからchrome.exeのフルパスを取得する
'Dim chromePath As String = GetChromeExecutablePath()
' パスが見つからない、またはその場所にファイルが存在しない場合は終了
If String.IsNullOrEmpty(chromePath) OrElse Not File.Exists(chromePath) Then
Return String.Empty
End If
' 2. 実行ファイルのバージョン情報を取得する
Try
Dim versionInfo As FileVersionInfo = FileVersionInfo.GetVersionInfo(chromePath)
Return versionInfo.FileVersion
Catch ex As Exception
' 何らかの理由でバージョン情報を取得できなかった場合
Console.WriteLine($"Chromeのバージョン情報取得に失敗しました: {ex.Message}")
Return String.Empty
End Try
End Function
End Class
呼び出し側はこんな感じです。
' --- 1. chromeWebDriverのバージョン確認&更新
Dim chormePath As String = "C:\Program Files\Google\Chrome\Application\chrome.exe"
Dim chromeVersion As String = "" '142.0.7444.163など
Dim platform As String = "win64" ' "win32", "mac-arm64" など環境に合わせて変更
chromeVersion = ChromeDriverFinder.GetInstalledChromeVersion(chormePath)
' URLを非同期で取得
Dim downloadUrl As String = Await ChromeDriverFinder.GetChromeDriverUrlAsync(chromeVersion, platform)
' --- 2. ファイルのダウンロード ---
' 保存先フォルダとファイル名を指定
Dim tempFolder As String = Path.GetTempPath() ' 一時フォルダに保存
Dim zipFileName As String = "chromedriver.zip"
Dim zipFilePath As String = Path.Combine(tempFolder, zipFileName)
Try
Using client As New HttpClient()
Dim fileBytes As Byte() = Await client.GetByteArrayAsync(downloadUrl)
File.WriteAllBytes(zipFilePath, fileBytes)
MessageBox.Show($"ZIPファイルをダウンロードしました: {zipFilePath}")
End Using
Catch ex As Exception
MessageBox.Show($"ファイルのダウンロード中にエラーが発生しました: {ex.Message}")
Return
End Try