在进行 Python UI 自动化测试时,Selenium 是一个非常强大的工具。然而,使用 Selenium 驱动 Chrome 浏览器时,必须确保 ChromeDriver 的版本与本地安装的 Chrome 浏览器版本匹配。如果版本不匹配,可能会导致脚本运行失败或异常。

手动下载和配置 ChromeDriver 版本不仅繁琐,还容易因版本更新而失效。本文将介绍一种优雅的解决方案,通过代码实现 ChromeDriver 驱动版本自动映射谷歌浏览器版本,从而彻底解决这一问题。


一、问题背景

1. 为什么需要匹配 ChromeDriver 和 Chrome 浏览器版本?

  • ChromeDriver 是谷歌官方提供的用于驱动 Chrome 浏览器的工具。
  • 每个版本的 ChromeDriver 只能支持特定范围内的 Chrome 浏览器版本。
  • 如果 ChromeDriver 和 Chrome 浏览器版本不匹配,可能会导致以下问题:
    • 启动浏览器失败;
    • 元素定位异常;
    • 测试脚本崩溃。

2. 常见的痛点

  • 手动下载 ChromeDriver 并配置路径,容易因版本更新而失效;
  • 不同环境(开发机、测试机、CI/CD 环境)可能安装了不同版本的 Chrome 浏览器;
  • 频繁更新 Chrome 浏览器后,需要同步更新 ChromeDriver,增加了维护成本。

二、解决方案:自动匹配 ChromeDriver 和 Chrome 浏览器版本

为了实现 ChromeDriver 和 Chrome 浏览器版本的自动匹配,我们可以编写一个完整的 Python 脚本。该脚本会自动检测当前系统中安装的 Chrome 浏览器版本,并根据版本号下载对应的 ChromeDriver。

以下是完整代码及详细解析:


1. 完整代码

import os
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from packaging import version
from webdriver_manager.chrome import ChromeDriverManager


def get_chrome_version():
    """
    获取当前安装的 Chrome 浏览器版本。
    :return: Chrome 浏览器的版本号(如 "133.0.6943.99")
    """
    try:
        # 通过读取注册表获取 Chrome 安装路径(适用于 Windows)
        import winreg
        try:
            key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe")
            chrome_path = winreg.QueryValue(key, None)
            key.Close()
        except FileNotFoundError:
            raise RuntimeError("未找到 Chrome 安装路径,请确保 Chrome 已正确安装。")

        print(f"检测到 Chrome 安装路径: {chrome_path}")

        # 提取版本号
        import subprocess
        result = subprocess.run([chrome_path, "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        version_info = result.stdout.strip()

        print(f"Chrome 版本命令输出: {version_info}")  # 调试信息

        # 提取版本号部分(支持多种格式)
        for part in version_info.split():
            if "." in part and part.replace(".", "").isdigit():  # 检查是否为版本号格式
                return part
        raise ValueError("无法从输出中提取 Chrome 版本号")
    except Exception as e:
        raise RuntimeError(f"无法获取 Chrome 浏览器版本: {e}")


def ensure_chromedriver_compatible(chromedriver_path: str):
    """
    检查指定路径下的 chromedriver 是否与当前 Chrome 浏览器版本匹配。
    如果不匹配,则自动下载并替换为符合浏览器版本的 chromedriver。
    
    :param chromedriver_path: 指定的 chromedriver 路径
    :return: 返回最终使用的 chromedriver 路径
    """
    # 获取当前 Chrome 浏览器的版本
    chrome_version = get_chrome_version()
    print(f"当前 Chrome 浏览器版本: {chrome_version}")

    # 检查指定路径下的 chromedriver 是否存在
    if os.path.exists(chromedriver_path):
        try:
            # 获取 chromedriver 的版本
            driver_version = get_chromedriver_version(chromedriver_path)
            print(f"当前 chromedriver 版本: {driver_version}")

            # 比较版本是否匹配
            if version.parse(driver_version) == version.parse(chrome_version):
                print("chromedriver 版本匹配,无需更新。")
                return chromedriver_path
            else:
                print("chromedriver 版本不匹配,需要更新。")
        except Exception as e:
            print(f"无法读取 chromedriver 版本,可能是文件损坏或无效: {e}")
            print("将重新下载 chromedriver。")
    else:
        print("指定路径下未找到 chromedriver,将下载最新版本。")

    # 下载并替换为符合浏览器版本的 chromedriver
    new_driver_path = download_and_replace_chromedriver(chromedriver_path, chrome_version)
    return new_driver_path


def get_chromedriver_version(chromedriver_path: str):
    """
    获取指定 chromedriver 的版本。
    :param chromedriver_path: chromedriver 的路径
    :return: chromedriver 的版本号(如 "133.0.6943.99")
    """
    try:
        from subprocess import Popen, PIPE
        process = Popen([chromedriver_path, "--version"], stdout=PIPE, stderr=PIPE)
        output, _ = process.communicate()
        version_info = output.decode("utf-8").strip()

        print(f"chromedriver 版本命令输出: {version_info}")  # 调试信息

        # 提取版本号部分(支持多种格式)
        for part in version_info.split():
            if "." in part and part.replace(".", "").isdigit():  # 检查是否为版本号格式
                return part
        raise ValueError("无法从输出中提取 chromedriver 版本号")
    except Exception as e:
        raise RuntimeError(f"无法获取 chromedriver 版本: {e}")


def download_and_replace_chromedriver(chromedriver_path: str, chrome_version: str):
    """
    下载符合 Chrome 浏览器版本的 chromedriver,并替换到指定路径。
    :param chromedriver_path: 目标 chromedriver 路径
    :param chrome_version: 当前 Chrome 浏览器版本
    :return: 新下载的 chromedriver 路径
    """
    print(f"正在下载符合 Chrome 版本 {chrome_version} 的 chromedriver...")
    manager = ChromeDriverManager(version="latest")  # 强制下载最新版本
    downloaded_path = manager.install()

    # 将下载的 chromedriver 替换到指定路径
    os.makedirs(os.path.dirname(chromedriver_path), exist_ok=True)
    os.replace(downloaded_path, chromedriver_path)
    print(f"已将 chromedriver 更新至路径: {chromedriver_path}")
    return chromedriver_path


if __name__ == "__main__":
    # 指定 chromedriver 的路径
    chromedriver_path = r"<你的 chromedriver 的路径 >"

    # 确保 chromedriver 与 Chrome 浏览器版本匹配
    final_driver_path = ensure_chromedriver_compatible(chromedriver_path)

    # 使用最终的 chromedriver 初始化 Selenium WebDriver
    service = Service(final_driver_path)
    driver = webdriver.Chrome(service=service)
    driver.get("https://www.google.com")
    print("浏览器已成功启动!")

2. 代码解析


(1)获取 Chrome 浏览器版本
  • 使用 winreg 模块读取 Windows 注册表:
    • 通过访问注册表路径 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe,获取 Chrome 浏览器的安装路径。
    • 如果未找到 Chrome 安装路径,抛出异常提示用户确保 Chrome 已正确安装。
  • 通过命令行提取版本号:
    • 使用 subprocess.run 执行命令 [chrome_path, "--version"],从命令行输出中提取 Chrome 浏览器的版本号。
    • 输出内容通常为类似 Google Chrome 133.0.6943.99 的字符串,通过字符串分割和正则匹配提取版本号部分(如 133.0.6943.99)。
  • 调试信息:
    • 打印 Chrome 的安装路径和版本号命令输出,便于排查问题。
(2)检查 ChromeDriver 版本
  • 验证指定路径下的 ChromeDriver 是否存在:
    • 使用 os.path.exists 检查指定路径下是否存在 ChromeDriver 文件。
    • 如果文件不存在,则直接跳过版本检查,进入下载流程。
  • 运行 chromedriver --version 命令:
    • 使用 subprocess.Popen 执行 [chromedriver_path, "--version"] 命令,获取当前 ChromeDriver 的版本号。
    • 输出内容通常为类似 ChromeDriver 133.0.6943.99 的字符串,通过字符串分割和正则匹配提取版本号部分。
  • 比较版本号:
    • 使用 packaging.version.parse 方法将 Chrome 浏览器版本和 ChromeDriver 版本转换为可比较的对象。
    • 如果两者版本号一致,则无需更新;否则,提示需要更新 ChromeDriver。
(3)自动下载 ChromeDriver
  • 使用 webdriver_manager.chrome.ChromeDriverManager 自动下载:
    • 调用 ChromeDriverManager(version="latest") 强制下载最新版本的 ChromeDriver。
    • 默认情况下,webdriver_manager 会根据 Chrome 浏览器版本自动匹配对应的 ChromeDriver 版本。
  • 支持国内镜像源:
    • 虽然代码中未显式指定镜像源,但可以通过修改 ChromeDriverManager 的 url 参数使用国内镜像(如淘宝镜像),以提高下载速度。
(4)更新 ChromeDriver
  • 替换到指定路径:
    • 使用 os.makedirs 确保目标路径的目录结构已创建。
    • 使用 os.replace 将下载的 ChromeDriver 文件移动到指定路径,覆盖旧文件。
  • 返回最终路径:
    • 返回更新后的 ChromeDriver 路径,供后续 Selenium WebDriver 使用。

运行逻辑总结

  1. 获取 Chrome 浏览器版本:
    • 通过注册表定位 Chrome 安装路径,并提取其版本号。
  2. 检查 ChromeDriver 版本:
    • 验证指定路径下的 ChromeDriver 是否存在,并比较其版本号与 Chrome 浏览器版本是否匹配。
  3. 自动下载并更新 ChromeDriver:
    • 如果版本不匹配或文件不存在,调用 webdriver_manager 下载最新版本的 ChromeDriver,并将其替换到指定路径。
  4. 启动 Selenium WebDriver:
    • 使用更新后的 ChromeDriver 初始化 Selenium WebDriver,打开目标网页。

三、运行效果

运行上述代码后,程序会自动完成以下操作:

  1. 检测当前系统中安装的 Chrome 浏览器版本;
  2. 检查指定路径下的 ChromeDriver 是否匹配;
  3. 如果不匹配,自动下载并替换为符合版本要求的 ChromeDriver;
  4. 使用更新后的 ChromeDriver 启动 Selenium WebDriver,打开目标网页。

四、注意事项

  1. 跨平台支持:

    • 当前代码主要针对 Windows 系统。如果需要支持 macOS 或 Linux,可以通过其他方式获取 Chrome 浏览器版本(如读取 /Applications/Google Chrome.app/Contents/Info.plist 文件)。
  2. 网络环境:

    • 如果处于内网环境,可以提前下载 ChromeDriver 并手动指定路径。
  3. 依赖库:

    • 确保安装了以下依赖库:
      pip install selenium webdriver-manager pywin32 packaging
      

五、总结

通过本文提供的解决方案,我们能够轻松实现 ChromeDriver 和 Chrome 浏览器版本的自动匹配,从而大幅提升 UI 自动化测试的稳定性和效率。相比手动管理驱动版本,这种方式更加智能、便捷,尤其适合多环境部署和持续集成场景。

希望本文对你有所帮助!如果你有任何疑问或建议,欢迎在评论区留言交流。


参考链接:

更多推荐