【问题标题】:How Do I Automatically Update the ChromeDriver or EdgeDriver in VBA?如何在 VBA 中自动更新 ChromeDriver 或 EdgeDriver?
【发布时间】:2021-06-16 04:00:58
【问题描述】:

我在网上搜索了自动“自动更新”我的 Selenium EdgeDriver 的解决方案。对于使用 SeleniumBasic 的用户,您知道当您的主主机浏览器进行重大更新时,每隔一段时间手动从相应网页下载驱动程序会很麻烦。

在我的网络搜索中,我发现支持其他语言“自动更新”他们的这些驱动程序的版本,但像往常一样,VBA 缺乏支持。

现在我不认为这是完美的解决方案,但它至少有效。我将来可以看到的问题是每个网页的布局可能会发生变化,所以如果是这种情况,我欢迎更新,我也会尝试更新。但在大多数情况下,它应该可以正常工作。

虽然这是一个自我回答的问题,但我绝对希望看到这里发布的其他方法供我自己和其他用户尝试。 SeleniumBasic 对于某些应用程序来说是一个很好的工具,但通常缺乏社区支持,因为 VBA 在社区中的使用不如其他语言那么广泛——至少在更复杂的层面上没有。

【问题讨论】:

  • 不熟悉 SeleniumBasic 的朋友可以到florentbr's Github here 下载。它自 2016 年以来没有更新,但它仍然运行良好。这将允许 MS Office 中的网络浏览器自动化。

标签: vba selenium selenium-webdriver selenium-chromedriver selenium-edgedriver


【解决方案1】:

首先我应该说我暂时只支持 Chrome 和 Edge 驱动程序。但是,如果您能跟随,您也许可以添加自己对任何其他 SeleniumBasic 支持的 WebDrivers 的支持。

在我们开始之前,重要的是通过转到 VBE 中的 Tools > References 来启用以下参考:

接下来,您需要创建一个名为SeleniumWebDriver类模块

我决定把它做成一个类对象,因为我打算在未来做一些事情。您可以根据需要添加自己的属性和函数,但提供的代码仅允许更新 WebDrivers,至少现在是这样。

这是完整的类模块代码:

Option Explicit

Rem Did Chrome change their file url and break your code?
' Check for an update: https://stackoverflow.com/a/67996166/5781745

Private ChromeDriver As Selenium.ChromeDriver
Private EdgeDriver As Selenium.EdgeDriver
Private SeleniumFolder As String
Private TempZipFile As String
Private ChromeInit As Boolean, EdgeInit As Boolean

Public Enum dType
    Chrome
    Edge
End Enum

Public Property Get SeleniumFolderPath() As String
    SeleniumFolderPath = SeleniumFolder
End Property

Public Property Let SeleniumFolderPath(ByVal FolderPath As String)
    SeleniumFolder = FolderPath
End Property
    
Public Sub UpdateDriver(ByVal DriverType As dType)

    'URLs to the drivers' home pages to which we can grab the curr versions
    Dim URLPath As String
    Select Case DriverType
    Case dType.Chrome
        URLPath = "https://chromedriver.chromium.org/home"
    Case dType.Edge
        URLPath = "https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/"
    End Select
    
    'Grab the current Version # from the driver's webpage
    Dim Doc As New HTMLDocument, DriverVer As String
    With New MSXML2.XMLHTTP60
        .Open "GET", URLPath
        .send
        Doc.body.innerHTML = .responseText
    End With
    DriverVer = getCurrentVersion(Doc, DriverType)
    
    DownloadUpdatedDriver DriverVer, DriverType
    ExtractZipAndCopy DriverType

End Sub

' For use in a later project. Not needed at this time
Private Sub InitializeDriver(ByVal DriverType As dType)
    Select Case DriverType
    Case dType.Chrome
        Set ChromeDriver = New Selenium.ChromeDriver
        ChromeDriver.Start
        ChromeInit = True
    Case dType.Edge
        Set EdgeDriver = New Selenium.EdgeDriver
        EdgeDriver.Start
        EdgeInit = True
    End Select
End Sub

Private Function getCurrentVersion(Doc As HTMLDocument, DriverType As dType) As String

    Dim div As HTMLDivElement

    Select Case DriverType
    Case dType.Chrome
        For Each div In Doc.getElementsByTagName("p")
            If div.innerText Like "Latest stable release*" Then
                With New VBScript_RegExp_55.RegExp
                    .Pattern = "ChromeDriver\s([\d\.]+)\b"
                    getCurrentVersion = .Execute(div.innerText)(0).SubMatches(0)
                    Exit Function
                End With
            End If
        Next
    Case dType.Edge
        With New VBScript_RegExp_55.RegExp
            .Pattern = "Version:\s([\d\.]+)"
            For Each div In Doc.getElementsByClassName("module")(0).getElementsByTagName("p")
                If .test(div.innerText) Then
                    getCurrentVersion = .Execute(div.innerText)(0).SubMatches(0)
                    Exit Function
                End If
            Next
        End With
    End Select

End Function

Private Sub DownloadUpdatedDriver(ByVal CurrVersion As String, DriverType As dType)
    
    Dim URLPath As String
    Select Case DriverType
    Case dType.Chrome
        URLPath = "https://chromedriver.storage.googleapis.com/" & CurrVersion & "/chromedriver_win32.zip"
    Case dType.Edge
        Kill Environ$("LocalAppData") & "\SeleniumBasic\Driver_Notes\*.*"
        URLPath = "https://msedgedriver.azureedge.net/" & CurrVersion & "/edgedriver_win64.zip"
    End Select
    
    Dim FileStream As New ADODB.Stream
    With New MSXML2.XMLHTTP60
        .Open "GET", URLPath
        .send
        FileStream.Open
        FileStream.Type = adTypeBinary
        FileStream.Write .responseBody
        FileStream.SaveToFile TempZipFile, adSaveCreateOverWrite
        FileStream.Close
    End With
    
End Sub

Private Sub ExtractZipAndCopy(ByVal DriverType As dType)

    Dim FileName As String
    Select Case DriverType
    Case dType.Chrome: FileName = "\chromedriver.exe"
    Case dType.Edge: FileName = "\edgedriver.exe"
    End Select

    'Delete the old WebDriver
    Kill SeleniumFolder & FileName
    
    'Copy the new driver from .zip file to SeleniumBasic folder
    Dim oShell As New shell
    oShell.Namespace(SeleniumFolder).CopyHere oShell.Namespace(TempZipFile).Items
    
    'Selenium VBA expects 'edgedriver' for edge, but new drivers are named 'msedgedriver'.
    'If we are updating Edge, we need to rename the file
    If DriverType = dType.Edge Then
        Name SeleniumFolder & "msedgedriver.exe" As SeleniumFolder & "edgedriver.exe"
    End If
        
    'Delete the temporary zip file
    Kill TempZipFile

End Sub

Private Sub Class_Initialize()

    ' Set the default file path. Can be modified later using ChromeDriverPath property
    SeleniumFolder = Environ$("LocalAppData") & "\SeleniumBasic\"
    TempZipFile = Environ$("LocalAppData") & "\Temp\WebDriver.zip"

End Sub

现在您已经创建了 Selenium 类,您现在可以在标准模块中使用它,例如:

重要提示:我不确定您更新网络浏览器和正式发布驱动程序之间是否存在延迟。因此,在更新您的驱动程序之前,我会先进行一些错误处理以查看 Selenium 是否会首先引发错误。如果驱动程序与浏览器版本不匹配,Selenium 将抛出错误 #33。如果您检查此错误,您应该可以安全地继续更新 WebDriver。我们要防止的是您在浏览器自动更新之前更新驱动程序,导致版本不匹配。

也有可能您的浏览器可能会更新,而 Selenium 驱动程序尚未发布 - 但不幸的是,这不是我们可以控制的。


这个答案的其余部分将详细说明它在做什么。如果你不介意,你现在可以离开了。

首先,与任何其他对象一样,我们必须对其进行初始化。在上面的示例中,我们使用 With New SeleniumWebDriver 语句来实现。这会在此处触发 Class_Initialize() 事件:

Private Sub Class_Initialize()

    ' Set the default file path. Can be modified later using ChromeDriverPath property
    SeleniumFolder = Environ$("LocalAppData") & "\SeleniumBasic\"
    TempZipFile = Environ$("LocalAppData") & "\Temp\WebDriver.zip"

End Sub

这样做的目的是为 SeleniumBasic 文件夹和临时文件设置默认文件路径。但是,如果您的文件夹位于其他位置,则此类具有您可以手动更改文件夹的属性。只需使用 ClassObj.SeleniumFolderPath() 属性来建立您的新路径。

TempZipFile 是一个类范围的变量,它将存储您从相应网站下载的 .zip 文件。

调用UpdateDriver 方法后,该类将向相应驱动程序的网页发出GET 请求,然后从页面中获取当前版本#。然后它将这个驱动程序版本传递给DownloadUpdatedDriver 例程,该例程存储每个驱动程序的下载链接。对于 Chrome,链接是:https://chromedriver.storage.googleapis.com/<Version#>/chromedriver_win32.zip,对于 Edge,它是:https://msedgedriver.azureedge.net/<Version#>/edgedriver_win64.zip。重要的是要意识到,如果您碰巧使用的是 32 位版本的 Edge,则需要将 URL 更改为 edgedriver_win32.zip。此例程将 .zip 文件下载到本地 AppData 的临时文件夹。

文件下载后,我们继续调用ExtractZipAndCopy 例程。这只是将 .exe 文件提取到 Selenium 文件夹,首先删除旧文件。 Edge 做了一些额外的维护工作,但你现在基本上已经更新了!

我希望这可以帮助那些因为我不得不定期更新这些驱动程序并想要一个自动化解决方案而烦恼的人。如果需要进行细微更改(例如 URL 损坏),请随时edit此答案。

【讨论】:

  • 您似乎没有对浏览器进行检查,这意味着您最终可以(当然在自动更新的情况下)使用 beta chrome 浏览器但稳定版本的 chromedriver。我确实看到了你的警告说明。您始终可以确定已安装的浏览器版本。我明白你为什么要检查 innerText 的稳定版本。它比仅使用 querySelector 更健壮,如果 html 稳定,则可以删除循环并将正则表达式应用于doc.querySelector("li > [dir] a").innerText
  • 当笔记本电脑不太热时我需要重新阅读其余部分可能会爆炸。添加 getCurrentVersion 的 ByVals 会引发什么?您是否会让 Windows 调度程序使用脚本运行每日检查以获取浏览器版本,然后检索适当的驱动程序(如果可用)?感谢分享+
  • 我确信有更简单的方法,但我知道这适用于 chrome 浏览器版本 https://www.ozgrid.com/forum/index.php?thread/72260-get-version-of-dll-file/。我替换为Filename = "chrome.exe"Directory = "C:\Program Files (x86)\Google\Chrome\Application\" 这也适用于确定当前的 chomedriver。在返回中你可以比较Product version:
【解决方案2】:

现在有一个 API 可以检查 ChromeDriver 的最新版本。所以代码可以变得更短。此外,仅当当前 chrome 版本与已安装的 chromedriver 版本不匹配时,才添加运行更新的功能。并使用管理员权限进行复制。 我没有使用 Edge,因此我的代码中没有代码功能。

Function chkchromever()
Tempfolder = "D:\"
TempZipFile = Tempfolder & "Chromedriver.zip"
SeleniumFolder = Environ$("ProgramW6432") & "\SeleniumBasic\"
TempDrvFile = Tempfolder & "Chromedriver.exe"
'Delete chromedriver.exe from temporary folder if it already exists
If Dir(TempDrvFile) <> "" Then
    Kill (TempDrvFile)
End If


Dim oShell  As New WshShell
Dim objHttp As Object
Set objHttp = CreateObject("MSXML2.ServerXMLHTTP.6.0")

'Get chrome version
chrversion = CreateObject("WScript.Shell").RegRead("HKEY_CURRENT_USER\Software\Google\Chrome\BLBeacon\version")
dotsarr = Split(chrversion, ".")
leftchrver = dotsarr(0) & dotsarr(1)

'Get chromedriver version
gspath = Chr(34) & SeleniumFolder & "chromedriver.exe" & Chr(34)
torun = gspath & " --version"
errcode = oShell.Exec(torun).StdOut.ReadAll
verarr = Split(errcode, " ")
chrdrv = verarr(1)
dotsarr2 = Split(chrdrv, ".")
leftchrdrv = dotsarr(0) & dotsarr(1)

'If major version mismatch (first two numbers) then ask if update required
If leftchrver <> leftchrdrv Then
    myyn = MsgBox("Wrong version of chromedriver. " & vbCrLf & "Chrome version is " & chrversion & vbCrLf & "Chrome driver version is " & chrdrv, vbYesNo, "Do you want to update Chromedriver ?")
    If myyn = vbNo Then Exit Function
    'Get latest release version of chromedriver which matches installed version of Chrome
    url = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_" & dotsarr(0)
    Call objHttp.Open("GET", url, False)
    Call objHttp.send("")
    version_number = objHttp.responseText
    dotsarr3 = Split(version_number, ".")
    leftversion_no = dotsarr3(0) & dotsarr3(1)
    If leftchrver = leftversion_no Then
        'If chromedriver found then download it
        download_url = "https://chromedriver.storage.googleapis.com/" + version_number + "/chromedriver_win32.zip"
        Call objHttp.Open("GET", download_url, False)
        Call objHttp.send("")
        Set fileStream = New ADODB.Stream
        With fileStream
            .Open
            .Type = adTypeBinary
            .Write objHttp.responseBody
            .Position = 0
            .SaveToFile TempZipFile, adSaveCreateOverWrite
            .Close
        End With
        'Copy the new driver from .zip file to SeleniumBasic folder
        Set oApp = CreateObject("Shell.Application")
        oApp.Namespace(Tempfolder).CopyHere oApp.Namespace(TempZipFile).Items
        
        'Create batch file to copy chromedriver.exe to Seleniumbasic folder and run it using Administrator rights
        tmpbatpath = Tempfolder & "copychdrv.bat"
        'Check if Chromedriver downloaded successfully
        If Dir(TempDrvFile) <> "" Then
            Open tmpbatpath For Output As #1
            'Enable these if required to copy chromedriver.exe
            '        Print #1, "taskkill /f /im  GoogleCrashHandler.exe"
            '        Print #1, "taskkill /f /im  GoogleCrashHandler64.exe"
            '        Print #1, "taskkill /f /im  Chrome.exe"
            '        Print #1, "taskkill /f /im  Googleupdate.exe"
            If IsProcessRunning("Chromedriver.exe") Then
                Print #1, "taskkill /f /im  Chromedriver.exe"
            End If
            Print #1, "copy " & Chr(34) & TempDrvFile & Chr(34) & " " & Chr(34) & SeleniumFolder & "Chromedriver.exe" & Chr(34) & "/y"
            Close #1
            'copy it now by running batch file
            success = ShellExecute(0, "runas", tmpbatpath, aPic, vbNullString, SW_SHOWNORMAL)
        End If
        'Cleanup
        If Dir(TempZipFile) <> "" Then
            Kill (TempZipFile)
        End If
        If Dir(tmpbatpath) <> "" Then
            Kill (tmpbatpath)
        End If
    End If
End If
End Function

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-17
    • 1970-01-01
    • 2022-12-14
    • 1970-01-01
    • 2018-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多