【问题标题】:How to autoupdate chromeDriver & geckDriver in selenium如何在 selenium 中自动更新 chromeDriver 和 geckDriver
【发布时间】:2019-08-17 07:47:15
【问题描述】:

我有多个节点机器的 selenium 网格设置,其中我在所有 selenium 节点机器上手动下载 chromeDriver 和 geckoDriver 并分别将它们用于 chrome 和 firefox 浏览器。

现在,chrome 和 firefox 浏览器(在所有 selenium 节点机器上)设置为“自动更新”(这是必需的,因为我希望我的应用程序始终在最新的浏览器版本上进行测试),因为 我的节点机器上的浏览器不断更新,因为相应的驱动程序更新是一个手动过程,它迫使我登录到每台 selenium 节点机器并手动更新它们

这个过程可以自动化吗?

PS:我知道 dockerized selenium grid 可用于获取/拉取最新的浏览器图像及其驱动程序,但是从传统的 selenium grid 切换到 dockerized selenium grid 是另一回事,需要一些时间来实现。

【问题讨论】:

  • 不确定你最后采取了什么方法,我也有类似的情况,找到了一个自动管理驱动程序的库:github.com/bonigarcia/webdrivermanager。我还没有探索这种方法,但如果有帮助,想给你发个便条..

标签: selenium selenium-chromedriver selenium-grid geckodriver


【解决方案1】:

我也有同样的情况。我想出了以下解决方案,通过检查 chrome 兼容性来自动更新驱动程序,

update_chromedriver.py

import zipfile

from bs4 import BeautifulSoup
import requests
import re
from selenium import webdriver
from selenium.common.exceptions import SessionNotCreatedException
from selenium.webdriver.chrome.options import Options

downloads_url = "https://chromedriver.chromium.org/downloads"
basic_downloads_path = 'https://chromedriver.storage.googleapis.com/{0}chromedriver_{1}.zip'
options = Options()
options.add_argument('--headless')
options.add_argument('--disable-gpu')

# os='win32'
os = 'linux64'
driver_executable = './driver/chromedriver'


def check_browser():
    driver = None
    try:
        driver = webdriver.Chrome(executable_path=driver_executable, options=options)
        driver.get('https://www.google.com/')
        return True
    except SessionNotCreatedException:
        return False
    finally:
        if driver is not None:
            driver.close()


def download_zip(_url):
    r = requests.get(_url, allow_redirects=True)
    open('./driver/chrome.zip', 'wb').write(r.content)
    with zipfile.ZipFile('./driver/chrome.zip') as z:
        z.extractall('./driver/')


def get_versions():
    html = requests.get(downloads_url).text
    soup = BeautifulSoup(html, "html.parser")
    versions_a = soup.find_all('a', href=re.compile('https://chromedriver.storage.googleapis.com/index.html\\?path='))
    versions = []
    for a in versions_a:
        version = a['href'].split('=', 1)[1]
        versions.append(version)
    return versions


def auto_update_driver():
    print('====> Checking current Chrome compatibility.')
    compatible = check_browser()
    print('====> It is ' + ('Compatible.' if compatible else 'Not Compatible.'))
    if not compatible:
        print('====> Trying to find alternate versions.')
        versions = get_versions()
        print(f'==> ({len(versions)}) found.')
        for version in versions:
            url = basic_downloads_path.format(version, os)
            download_zip(url)
            compatible = check_browser()
            print(f'==> Trying version {version} - {"Compatible" if compatible else "Not Compatible"}')
            if compatible:
                break
        if not compatible:
            raise Exception('Unable to upgrade chrome version. Probably you are using very old chrome version.'
                            'Please update chrome and try again!!')

请注意:

  1. 我的chrome驱动在当前路径./driver/
  2. 这在我的 ubuntu 机器上运行。请根据您的要求进行相应更改

上述python脚本的使用

from update_chromedriver import auto_update_driver

auto_update_driver()

【讨论】:

    【解决方案2】:

    一种可能的解决方案 - 编写一个脚本来下载最新的 Webdriver 版本并安排其每日发布。

    例如,这里是 Python 3 脚本,用于下载与当前安装的 Chrome 浏览器版本匹配的最新 Chromedriver for Linux

    import os
    import re
    import requests
    import zipfile
    
    XML_INFO_URL = 'https://chromedriver.storage.googleapis.com/LATEST_RELEASE_'
    DRIVER_PATH = 'https://chromedriver.storage.googleapis.com'
    ZIPPED_DRIVER_FILE_NAME = 'chromedriver_linux64.zip'
    UNZIPPED_DRIVER_FILE_NAME = 'chromedriver'
    
    PATH_TO_CHROMEDRIVER = '.'
    
    def get_browser_major_version():
        stream = os.popen('google-chrome --version')
        output = stream.read()
        version_info_str = re.search(r'\d+\.\d+\.\d+', output).group(0)
        return re.search(r'^\d+', version_info_str).group(0)
    
    
    def get_driver_latest_version(browser_major_version):
        return requests.get(XML_INFO_URL + browser_major_version).text
    
    
    def download_file(url, file_name):
        file = requests.get(url)
        with open(file_name, "wb") as code:
            code.write(file.content)
    
    
    driver_file_url = DRIVER_PATH + '/' + get_driver_latest_version(get_browser_major_version()) + '/' + ZIPPED_DRIVER_FILE_NAME
    download_file(driver_file_url, ZIPPED_DRIVER_FILE_NAME)
    
    with zipfile.ZipFile(ZIPPED_DRIVER_FILE_NAME, 'r') as zip_ref:
        zip_ref.extractall(PATH_TO_CHROMEDRIVER)
    
    os.chmod(PATH_TO_CHROMEDRIVER + '/' + UNZIPPED_DRIVER_FILE_NAME, 0o744)
    
    os.remove(ZIPPED_DRIVER_FILE_NAME)
    

    【讨论】:

      【解决方案3】:

      基于 Java 的解决方案的一个选项是 Bonigarcia Webdrivermanager。似乎它内置了对远程 Web 驱动程序的支持,使用这个驱动程序管理器可以在 selenium 网格的 Hub 上下载最新的浏览器。查看this 的帖子。

      【讨论】:

        【解决方案4】:

        First @Asyranok is right,即使实现了自动更新代码也不会 100% 工作。但是,对于我们中的许多人来说,这种偶尔的停机时间是“可以的”,只要它只是几天。

        我发现每隔几个月手动更新 X 服务器非常令人恼火,虽然 selenium 网站上有 well written instructions 关于如何“自动更新”驱动程序,但我还没有看到一个公开可用的非本指南的库实现。

        我的回答是针对 C# 的,对于这种语言,通常建议的解决方案是 use NuGet 自动拉取最新的驱动程序,这有两个问题:

        1. 您需要按照 chrome 更新的频率进行部署(大多数公司还没有,我们也没有),否则您的应用程序将在 chrome 更新和应用程序的“新”版本部署之间的时间里“损坏” ,而且这只是在您按计划发布的情况下,如果您临时发布,您将不得不通过一系列手动步骤来更新、构建、发布等,以使您的应用程序再次运行。

        2. 您需要(通常是without a work around)手动从 NuGet 中提取最新的 chromedrive,这也是一个手动过程。

        python 所拥有的和 @leminhnguyenHUST suggests 使用的库会在运行时自动拉取最新的 chromedriver,这会更好。我环顾四周,还没有发现任何 C# 可以做到这一点,所以我决定推出自己的并将其构建到我的应用程序中:

        public void DownloadLatestVersionOfChromeDriver()
        {
            string path = DownloadLatestVersionOfChromeDriverGetVersionPath();
            var version = DownloadLatestVersionOfChromeDriverGetChromeVersion(path);
            var urlToDownload = DownloadLatestVersionOfChromeDriverGetURLToDownload(version);
            DownloadLatestVersionOfChromeDriverKillAllChromeDriverProcesses();
            DownloadLatestVersionOfChromeDriverDownloadNewVersionOfChrome(urlToDownload);
        }
        
        public string DownloadLatestVersionOfChromeDriverGetVersionPath()
        {
            //Path originates from here: https://chromedriver.chromium.org/downloads/version-selection            
            using (RegistryKey key = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe"))
            {
                if (key != null)
                {
                    Object o = key.GetValue("");
                    if (!String.IsNullOrEmpty(o.ToString()))
                    {
                        return o.ToString();
                    }
                    else
                    {
                        throw new ArgumentException("Unable to get version because chrome registry value was null");
                    }
                }
                else
                {
                    throw new ArgumentException("Unable to get version because chrome registry path was null");
                }
            }
        }
        
        public string DownloadLatestVersionOfChromeDriverGetChromeVersion(string productVersionPath)
        {
            if (String.IsNullOrEmpty(productVersionPath))
            {
                throw new ArgumentException("Unable to get version because path is empty");
            }
        
            if (!File.Exists(productVersionPath))
            {
                throw new FileNotFoundException("Unable to get version because path specifies a file that does not exists");
            }
        
            var versionInfo = FileVersionInfo.GetVersionInfo(productVersionPath);
            if (versionInfo != null && !String.IsNullOrEmpty(versionInfo.FileVersion))
            {
                return versionInfo.FileVersion;
            }
            else
            {
                throw new ArgumentException("Unable to get version from path because the version is either null or empty: " + productVersionPath);
            }
        }
        
        public string DownloadLatestVersionOfChromeDriverGetURLToDownload(string version)
        {
            if (String.IsNullOrEmpty(version))
            {
                throw new ArgumentException("Unable to get url because version is empty");
            }
        
            //URL's originates from here: https://chromedriver.chromium.org/downloads/version-selection
            string html = string.Empty;
            string urlToPathLocation = @"https://chromedriver.storage.googleapis.com/LATEST_RELEASE_" + String.Join(".", version.Split('.').Take(3));
        
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlToPathLocation);
            request.AutomaticDecompression = DecompressionMethods.GZip;
        
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            using (Stream stream = response.GetResponseStream())
            using (StreamReader reader = new StreamReader(stream))
            {
                html = reader.ReadToEnd();
            }
        
            if (String.IsNullOrEmpty(html))
            {
                throw new WebException("Unable to get version path from website");
            }
        
            return "https://chromedriver.storage.googleapis.com/" + html + "/chromedriver_win32.zip";
        }
        
        public void DownloadLatestVersionOfChromeDriverKillAllChromeDriverProcesses()
        {
            //It's important to kill all processes before attempting to replace the chrome driver, because if you do not you may still have file locks left over
            var processes = Process.GetProcessesByName("chromedriver");
            foreach (var process in processes)
            {
                try
                {
                    process.Kill();
                }
                catch
                {
                    //We do our best here but if another user account is running the chrome driver we may not be able to kill it unless we run from a elevated user account + various other reasons we don't care about
                }
            }
        }
        
        public void DownloadLatestVersionOfChromeDriverDownloadNewVersionOfChrome(string urlToDownload)
        {
            if (String.IsNullOrEmpty(urlToDownload))
            {
                throw new ArgumentException("Unable to get url because urlToDownload is empty");
            }
        
            //Downloaded files always come as a zip, we need to do a bit of switching around to get everything in the right place
            using (var client = new WebClient())
            {
                if (File.Exists(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\chromedriver.zip"))
                {
                    File.Delete(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\chromedriver.zip");
                }
        
                client.DownloadFile(urlToDownload, "chromedriver.zip");
        
                if (File.Exists(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\chromedriver.zip") && File.Exists(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\chromedriver.exe"))
                {
                    File.Delete(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\chromedriver.exe");
                }
        
                if (File.Exists(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\chromedriver.zip"))
                {
                    System.IO.Compression.ZipFile.ExtractToDirectory(System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\chromedriver.zip", System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location));
                }
            }
        }
        

        然后通常我会在我的应用程序开始时坚持这个非常hacky的调用来调用这个功能并确保我的应用程序可以使用最新的chromedriver:

        //This is a very poor way of determining if I "need" to update the chromedriver,     
        //however I've yet to figure out a better way of doing this...
        try
        {
            using (var chromeDriver = SetupChromeDriver())
            {
                chromeDriver.Navigate().GoToUrl("www.google.com");
                chromeDriver.Quit();
            }
        }
        catch
        {
            DownloadLatestVersionOfChromeDriver();
        }
        

        我确信这可以得到显着改善,但到目前为止它对我有用。

        注意:交叉发布Here

        【讨论】:

        • 这对我来说就像一个魅力。谢谢。
        【解决方案5】:

        我知道这是一个老问题,但我认为这对人们也有帮助:https://github.com/rosolko/WebDriverManager.Net。它是一个 Nuget 包 (https://www.nuget.org/packages/WebDriverManager/),似乎可以使用 .NET 解决问题。

        【讨论】:

          【解决方案6】:

          您可以使用 ansible 或 puppet 在所有节点上获取更新包

          【讨论】:

          【解决方案7】:

          现在问题已经通过来自herewebdrivermanager 模块解决了

          说明:

          便于下载和部署 WebDriver 二进制文件的 Python 模块。此模块中的类可用于自动搜索和下载 WebDriver 二进制文件的最新版本(或特定版本),然后通过复制或符号链接将其提取并放置到 Selenium 或其他工具应该能够的位置然后找到它。

          希望对你有帮助!!!

          【讨论】:

            【解决方案8】:

            我认为您当前的方法不是可行的方法。新版本的浏览器发布时对 Selenium(或任何其他驱动程序)零考虑。一旦发布了新的浏览器更新,很有可能没有现有的驱动程序适用于该版本。 Selenium 团队通常需要几天时间才能发布更新的驱动程序以匹配最新版本的浏览器。

            而且由于您会自动更新浏览器,因此您可能会自动中断 Selenium 测试,直到发布新的驱动程序版本,或者直到您降级浏览器。

            现在,您可能会对此感到满意,并且可以禁用浏览器的测试,直到最新的 Selenium 驱动程序与最新的浏览器版本一起工作。如果是这种情况,那么这里有一些解决方案:

            1) 如果您使用 C#,请将您的驱动程序作为 Nuget 包或依赖项文件夹存储在测试解决方案中。然后,无论它在哪里运行,都有该驱动程序的自动化参考。当您需要更新驱动程序时,您实际上只需要在一个地方更新它,然后检查更改。所有客户端机器都将通过您的 CI 流程下载最新代码,其中包括该新驱动程序。

            2) 如果出于某种原因您不希望项目中的驱动程序作为 Nuget 包或手动保存的依赖项,则让您的 CI 处理更新过程。将您的自动化代码指向一个驱动程序,该驱动程序位于当前正在运行的任何客户端计算机上的某个公共目录中 -> 您的计算机在下载依赖项后存储依赖项的任何位置。例如;在 Windows 机器上通过控制台下载 selenium 文件会将它们放在 %APPDATA% "C:\Users\xxxxxx\AppData\Roaming\npm\node_modules" 的某个位置。这就是您的测试解决方案应该看的地方。

            然后,在您的 CI 脚本中,在运行任何测试之前,下载最新的驱动程序。 Windows 和 Linux/Unix 内核之间的语法几乎相同,即使不相同。这假设您已安装 npm。

            npm install -g selenium
            

            如果你已经有最新的,那么什么都不会发生。如果您不这样做,则 CI 脚本将在运行测试之前下载最新的驱动程序。然后,您的测试解决方案将指向驱动程序在客户端上的存储位置,并且会自动使用最新的驱动程序。

            【讨论】:

            • 感谢您详细解答。珍惜你的时间。在这里,我通过 jenkins 运行我的测试,所以架构/流程就像 jenkins(触发 selenium 套件)-> jenkins slave machine(拉取包含测试的 qa repo,通过 gradle/maven 运行它们)-> selenium hub machine -> selenium nodes machine (有 geckodriver/chromedriver)考虑到上面的架构,让我思考一下上面的解决方案,
            • #1 建议在测试 repo 中打包驱动程序(chrome/gecko),如果我这样做,它将留在我的 jenkins slave 机器上,因为 jenkins slave 是拉 qa repo 的机器,但是我想要那些驱动程序存在于 selenium 节点机器上。因此,在我的情况下,使用 git/nuget 管理器打包驱动程序将无济于事(但是将这些驱动程序保存在某个共享文件夹中,所有 selenium 节点都可以访问该文件夹,然后在发布新驱动程序后手动更新共享文件夹下的驱动程序将起作用,但是仍然是手动操作)
            • #2 建议在通过 jenkins 运行测试之前使用 NPM 更新 selenium/chromedriver/geckodriver(如 npm install chromedriver / npm install geckodriver)。我必须通过将它们作为 jenkins 从机来专门在 selenium 节点上运行这些命令(即 selenium 节点机器应该充当 jenkins 从机,以便我可以在它们上执行命令),但是 NPM 将是安装在每台 selenium 节点机器上的先决条件.好吧,我会尝试这两种选择,看看哪种更适合我。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-10-31
            • 2015-01-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-08-14
            相关资源
            最近更新 更多