1. 为什么你需要用代码来更新浏览器驱动
做自动化测试或爬虫开发的朋友,应该都对浏览器驱动不陌生。拿Selenium来说,无论是chromedriver、geckodriver还是msedgedriver,它们本质上是浏览器与自动化代码之间的“翻译官”,负责把脚本里的指令转成浏览器能理解的底层操作。
但驱动这个东西有个让人头疼的特性:跟浏览器版本强绑定。浏览器一个大版本升级,旧驱动直接失效,脚本里各种session not created、This version of ChromeDriver only supports Chrome version XX的报错就会冒出来。过去我手动下载驱动,流程一般是先打开浏览器,输入官方下载地址,再去翻版本列表,找到匹配版本后解压、复制到指定目录,整套流程走下来差不多要十来分钟。如果手头管着多台机器、多个浏览器,这个重复劳动的时间成本就很可观了。
用代码方式自动下载驱动,核心解决的问题就是版本匹配和流程自动化。脚本自己读取本地浏览器版本,然后去驱动仓库拉取对应版本,下载、解压、放到指定位置,一条命令全搞定。这不仅省时间,更重要的是避免了人肉比对版本可能出现的疏漏。
这篇内容适合以下人群阅读:
- 使用 Selenium、Playwright(驱动部分)做自动化测试的测试工程师
- 写爬虫脚本经常换环境重装依赖的开发者
- 维护多台测试设备、需要批量同步驱动版本的同学
我会从原理、代码实现到踩坑排查,完整地把这套方案讲清楚。
2. 版本匹配的底层逻辑:驱动和浏览器到底怎么对应上的
要把自动下载这事做好,先得搞清楚驱动和浏览器的版本对应关系是怎么来的。这个机制理清了,后面写代码才有依据。
2.1 主版本号是硬约束
以 Chrome 和 ChromeDriver 为例,浏览器版本号一般长这样:114.0.5735.90。ChromeDriver 的版本号规则与之对应,比如114.0.5735.16。实际运行时,ChromeDriver 要求浏览器的主版本号(第一位数字)跟自己一致,也就是 Chrome 主版本是 114,那么驱动主版本也必须是 114。至于后面的小版本号,驱动的兼容性做得比较宽松,不一定要求逐位对齐。
这一点是很多新手踩坑的地方。比如浏览器刚升级到 115,驱动还是 114 的,那启动时几乎必然报版本不匹配的错误。反过来,浏览器是 114.0.5735.90,驱动用的是 114.0.5735.16,通常能正常工作。
2.2 Edge 的特殊性
Edge 的情况稍微特殊一点,它现在基于 Chromium 内核,所以驱动机制和 Chrome 类似,版本号规则也基本一致。不过它叫msedgedriver,下载来源是微软的官方渠道,不是谷歌的。自动下载时,需要单独适配它的版本接口和下载地址。
2.3 Firefox 的宽松策略
Firefox 的geckodriver相对宽松,它跟 Firefox 版本不是严格一一对应,而是有一个支持范围。比如 geckodriver 0.33.0 可能支持 Firefox 102 到 119 的多个版本。所以自动下载 Firefox 驱动时,通常只需要获取最新版本的 geckodriver 就行,不像 Chrome 那样要逐个版本去匹配。
理解了这些规则,就能明白为什么自动下载方案里,Chrome 需要先查浏览器版本再精确匹配,而 Firefox 可以简单粗暴地直接拉最新版。
3. 核心思路拆解:一次完整的自动下载要分几步走
写自动下载脚本之前,先在脑子里把整个流程拆清楚。我最初第一版脚本写得稀烂,就是因为没把流程理顺,东拼西凑,导致各种边缘情况处理不了。后来重新梳理,发现其实就四步:
- 读取本地浏览器版本:从注册表、配置文件或浏览器安装目录里拿到当前安装的浏览器版本号。
- 向驱动仓库查询对应版本:把浏览器版本号传给驱动的版本查询接口,拿到匹配的驱动版本号和下载地址。
- 下载并校验文件:根据下载地址拉取驱动压缩包,校验文件完整性。
- 解压并按规则放置:解压驱动文件到指定目录,配置好环境变量或路径引用。
这四步每一步都有一些细节处理,我分别说一下。
3.1 读取浏览器版本:别只盯着一种方式
Windows 系统上,Chrome 的版本信息一般存储在注册表里。最常见的路径是:
HKEY_CURRENT_USER\Software\Google\Chrome\BLBeacon这里面有个version字段,记录了当前 Chrome 的完整版本号。
但注册表路径不是一定不变的。如果 Chrome 是按系统级安装的,需要在HKEY_LOCAL_MACHINE\SOFTWARE\Google\Chrome\BLBeacon里找。有些定制版、绿色版 Chrome 甚至没有注册表项,这时候就得去安装目录看版本号,比如 Chrome 的chrome.exe文件属性里的“产品版本”字段。
所以代码里我一般按优先级去尝试多个来源:
- 先查当前用户的注册表项
- 再查系统级注册表项
- 最后尝试从可执行文件属性读取版本号
这样覆盖面比较全,不容易漏。
3.2 驱动版本查询:Chrome 和 Edge 有现成接口
ChromeDriver 官方提供了一个 JSON 格式的版本列表接口,地址是:
https://chromedriver.storage.googleapis.com/LATEST_RELEASE_{版本号}比如浏览器版本是114.0.5735.90,请求LATEST_RELEASE_114.0.5735,返回的就是该版本对应的最新 ChromeDriver 版本号。拿到版本号后,构造下载地址:
https://chromedriver.storage.googleapis.com/{驱动版本号}/chromedriver_win32.zipLinux 和 Mac 则对应chromedriver_linux64.zip、chromedriver_mac64.zip(新版还有mac-arm64)。
Edge 的驱动下载地址结构不太一样,微软的接口是:
https://msedgedriver.azureedge.net/LATEST_RELEASE_{版本号}同样是传入浏览器版本号,返回匹配的驱动版本,然后从这个地址下载:
https://msedgedriver.azureedge.net/{驱动版本号}/edgedriver_win64.zip注意 Edge 的压缩包命名和 Chrome 不一样,而且 Windows 版有win32和win64两种,要根据系统实际情况选择。
3.3 下载和解压:注意代理和网络环境
下载这块,Python 的requests库是最顺手的选择。但实际开发环境里经常遇到两个问题:一是公司内网需要走代理,二是下载超时。
代理问题可以在requests.get()里显式传入proxies参数,或者通过环境变量设置。超时问题则需要设置合理的timeout值,驱动包一般不大,几 MB 到十几 MB,给个 30 秒到 60 秒通常够了。
解压方面,Windows 自带zipfile模块就能处理。但要注意,压缩包里可能包含多个文件,比如LICENSE.chromedriver这类说明文件,真正要的只是chromedriver.exe。解压时最好只提取目标驱动文件,或者解压后做一个筛选,避免把一堆无关文件丢到目录里。
4. 代码实现:从零开始写一个驱动自动更新脚本
方案理清楚了,代码写起来就顺了。我下面用 Python 写一个完整的示例,覆盖 Chrome 和 Edge 的自动下载,需要的库只有requests、winreg(Windows 专属)和zipfile这几个。
4.1 读取浏览器版本的实现
import re import subprocess import winreg from pathlib import Path def get_chrome_version(): """读取本机 Chrome 版本号,按优先级尝试多个来源""" # 方式一:当前用户注册表 try: key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Google\Chrome\BLBeacon") value, _ = winreg.QueryValueEx(key, "version") winreg.CloseKey(key) if value: return value.strip() except FileNotFoundError: pass # 方式二:系统级注册表 try: key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Google\Chrome\BLBeacon") value, _ = winreg.QueryValueEx(key, "version") winreg.CloseKey(key) if value: return value.strip() except FileNotFoundError: pass # 方式三:从 chrome.exe 文件属性读取 chrome_path = r"C:\Program Files\Google\Chrome\Application\chrome.exe" if Path(chrome_path).exists(): try: cmd = ( f'powershell -command "(Get-Item \'{chrome_path}\').VersionInfo.ProductVersion"' ) result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=10) if result.returncode == 0: return result.stdout.strip() except Exception: pass raise RuntimeError("无法获取 Chrome 版本号,请手动确认浏览器安装情况") def get_edge_version(): """读取本机 Edge 版本号""" try: key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Edge\BLBeacon") value, _ = winreg.QueryValueEx(key, "version") winreg.CloseKey(key) if value: return value.strip() except FileNotFoundError: pass raise RuntimeError("无法获取 Edge 版本号")这段代码把版本读取的几种途径都覆盖了。实际运行中,绝大多数机器走的是方式一就能拿到结果。方式三用 PowerShell 读文件属性,速度稍慢,但胜在兼容性好,适合那些注册表项异常的环境。
4.2 查询驱动版本并构造下载地址
import requests def get_chromedriver_version(browser_version): """根据 Chrome 版本查询匹配的 ChromeDriver 版本""" # 去掉最后一段小版本号,接口需要的是前三位 parts = browser_version.split(".") if len(parts) > 3: query_version = ".".join(parts[:3]) else: query_version = browser_version url = f"https://chromedriver.storage.googleapis.com/LATEST_RELEASE_{query_version}" resp = requests.get(url, timeout=30) resp.raise_for_status() return resp.text.strip() def get_edgedriver_version(browser_version): """根据 Edge 版本查询匹配的 EdgeDriver 版本""" parts = browser_version.split(".") if len(parts) > 3: query_version = ".".join(parts[:3]) else: query_version = browser_version url = f"https://msedgedriver.azureedge.net/LATEST_RELEASE_{query_version}" resp = requests.get(url, timeout=30) resp.raise_for_status() return resp.text.strip() def get_chromedriver_download_url(driver_version, platform="win64"): """构造 ChromeDriver 下载地址""" base = "https://chromedriver.storage.googleapis.com" return f"{base}/{driver_version}/chromedriver_{platform}.zip" def get_edgedriver_download_url(driver_version, platform="win64"): """构造 EdgeDriver 下载地址,新版命名规则略有不同""" base = "https://msedgedriver.azureedge.net" return f"{base}/{driver_version}/edgedriver_{platform}.zip"这里有个细节要注意:Chrome 官方的LATEST_RELEASE接口,接受的是114.0.5735这种三位版本号格式。如果你直接把完整的114.0.5735.90传进去,有时也能返回结果,但为了稳妥,最好截取前三位。Edge 的接口规则也类似。
platform参数需要根据系统实际选择,Windows x64 系统用win64,Windows x86 用win32,macOS 根据芯片架构选mac64或mac-arm64,Linux 用linux64。
4.3 下载、解压和存放的完整实现
import os import sys import zipfile import tempfile def download_and_extract(url, target_dir): """ 下载驱动压缩包并解压到目标目录 返回解压后的驱动文件完整路径 """ # 创建临时文件用于下载 with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp: tmp_name = tmp.name try: # 流式下载,避免一次加载到内存导致机器卡顿 resp = requests.get(url, stream=True, timeout=60) resp.raise_for_status() total_size = int(resp.headers.get("content-length", 0)) downloaded = 0 with open(tmp_name, "wb") as f: for chunk in resp.iter_content(chunk_size=8192): f.write(chunk) downloaded += len(chunk) # 简单打印进度,实际脚本里可以去掉 if total_size > 0: percent = round(downloaded / total_size * 100, 1) print(f"\r下载进度: {percent}%", end="", flush=True) print("\n下载完成,开始解压") # 解压并筛选目标文件 target_dir = Path(target_dir) target_dir.mkdir(parents=True, exist_ok=True) # 识别驱动文件名 if "chromedriver" in url: driver_name = "chromedriver" elif "edgedriver" in url: driver_name = "msedgedriver" else: driver_name = "geckodriver" # Windows 下需要加上 .exe 后缀 if sys.platform == "win32": driver_name += ".exe" extracted_path = None with zipfile.ZipFile(tmp_name, "r") as zf: for name in zf.namelist(): # 只解压目标驱动文件,跳过 LICENSE、README 等辅助文件 if name.endswith(driver_name) or Path(name).name == driver_name: zf.extract(name, target_dir) extracted_path = target_dir / Path(name).name break if extracted_path is None: # 没找到目标文件时,老老实实全部解压 zf.extractall(target_dir) extracted_path = target_dir / driver_name return str(extracted_path) finally: # 清理临时文件 if os.path.exists(tmp_name): os.remove(tmp_name)上面这段代码里有个容易被忽略的细节:用Path(name).name == driver_name做匹配,而不是直接用name.endswith(driver_name)。这是因为压缩包内部可能有多层目录结构,直接取文件名更稳妥。比如某些版本的 driver 压缩包里,可执行文件在根目录,有些则在子目录里。筛选逻辑写得好,才能在各种压缩包结构下都能找对文件。
4.4 串起来的主流程
def ensure_driver(browser="chrome", save_dir=None): """ 统一入口:检查本机驱动是否匹配,不匹配则自动下载更新 """ if save_dir is None: save_dir = os.getcwd() if browser == "chrome": browser_version = get_chrome_version() print(f"检测到 Chrome 版本: {browser_version}") driver_version = get_chromedriver_version(browser_version) print(f"匹配的 ChromeDriver 版本: {driver_version}") existing_driver = Path(save_dir) / "chromedriver.exe" # 如果驱动已存在且版本匹配,直接跳过下载 if existing_driver.exists(): current = get_local_driver_version(existing_driver) if current == driver_version: print("本地驱动已是最新,无需更新") return str(existing_driver) platform = "win64" if sys.platform == "win32" else "linux64" url = get_chromedriver_download_url(driver_version, platform) path = download_and_extract(url, save_dir) print(f"ChromeDriver 已就绪: {path}") return path elif browser == "edge": browser_version = get_edge_version() print(f"检测到 Edge 版本: {browser_version}") driver_version = get_edgedriver_version(browser_version) print(f"匹配的 EdgeDriver 版本: {driver_version}") existing_driver = Path(save_dir) / "msedgedriver.exe" if existing_driver.exists(): current = get_local_driver_version(existing_driver) if current == driver_version: print("本地 EdgeDriver 已是最新,无需更新") return str(existing_driver) platform = "win64" if sys.platform == "win32" else "linux64" url = get_edgedriver_download_url(driver_version, platform) path = download_and_extract(url, save_dir) print(f"EdgeDriver 已就绪: {path}") return path else: raise ValueError(f"不支持的浏览器类型: {browser}")主流程里我加了一个版本预检步骤,也就是get_local_driver_version这个函数(下面会说实现)。它能读取本地已有的驱动版本,如果和期望版本一致,就直接复用,省去重复下载。这个优化在多台机器、多次跑脚本时非常有用。
4.5 读取本地驱动版本的辅助函数
def get_local_driver_version(driver_path): """读取本地驱动文件的版本号""" if sys.platform == "win32": try: cmd = f'powershell -command "(Get-Item \'{driver_path}\').VersionInfo.FileVersion"' result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=10) if result.returncode == 0: return result.stdout.strip() except Exception: pass else: # Linux/macOS 可以尝试执行 --version 参数 try: result = subprocess.run( [str(driver_path), "--version"], capture_output=True, text=True, timeout=10, ) if result.returncode == 0: # 输出形如 "ChromeDriver 114.0.5735.16 ..." parts = result.stdout.strip().split() if len(parts) >= 2: return parts[1] except Exception: pass return NoneWindows 上读取 exe 的文件版本号,用 PowerShell 的Get-Item最直接。FileVersion属性返回的格式刚好是114.0.5735.16这样的完整版本号,可以直接拿来和驱动仓库返回的版本比较。
Linux 和 macOS 上的可执行文件没有文件版本属性,但绝大多数驱动支持--version参数,输出第一行里包含版本号。解析的时候注意不同驱动的输出格式有差异,比如 geckodriver 输出的是geckodriver 0.33.0,而 chromedriver 输出的是ChromeDriver 114.0.5735.16,所以取第二个空格分隔的字段通常能拿到版本号。
4.6 完整脚本的调用示例
if __name__ == "__main__": driver_path = ensure_driver(browser="chrome", save_dir=r"D:\drivers") print(f"驱动路径: {driver_path}") # 后续就可以把这个路径直接传给 Selenium # from selenium import webdriver # service = webdriver.ChromeService(executable_path=driver_path) # driver = webdriver.Chrome(service=service)这里有一点要重点说明:下载路径save_dir是放驱动文件的目录,建议用一个独立的、固定的目录,不要放在 Selenium 脚本所在的项目根目录。因为驱动文件更新频率高于业务代码,单独放一个目录,以后升级驱动不会把项目目录搞得乱七八糟。
5. 工具选型和环境差异:不同场景下的适配方案
上面的脚本是我最常用的一套方案。但实际项目里,不同团队的技术栈不同,统一的 Python 脚本并不总适用。这里我补充几种常见场景的选型建议。
5.1 不用 Python?换种语言也一样
有的团队测试体系基于 Java 或 JavaScript 生态,不想额外引入 Python 依赖。其实核心逻辑是通用的,无非是“发 HTTP 请求、读版本、解压文件”这几件事,任何语言都能实现。
Java 里可以用java.net.http.HttpClient或者 Apache HttpClient,读取注册表可以用Preferences类(Windows 平台),解压用ZipInputStream。Node.js 里可以用axios或内置的fetch发请求,读注册表用winreg包,解压用decompress或adm-zip。
选型的基本原则是:尽量用团队已有的技术栈,避免为一个小工具引入整套新语言生态。这个问题我和不少团队交流过,很多运维和测试同学的第一反应是“我只会 Java,Python 写不来”,结果就卡在语言选择上迟迟没落地。其实换语言只是把上面几个函数翻译一遍,核心逻辑完全一致。
5.2 Linux/macOS 环境的差异处理
上面的脚本里已经做了部分平台判断,但有几点需要单独说明:
Linux 环境下读取 Chrome 版本,没有注册表可查,可以尝试通过命令行:
google-chrome --version # 或者 chromium-browser --version输出形如Google Chrome 114.0.5735.90,按空格切分取第三位就是版本号。
macOS 环境下,Chrome 的应用路径一般是/Applications/Google Chrome.app/Contents/MacOS/Google Chrome,也可以用--version参数获取版本。
驱动文件名在 Linux/macOS 下没有.exe后缀,压缩包里解压出来的是一个无扩展名的可执行文件。这一点脚本里通过sys.platform判断已经处理了,但如果手写其他语言的版本,很容易漏掉这个细节。
另外,Linux/macOS 下解压后的驱动文件需要chmod +x赋予执行权限,否则 Selenium 启动时会报权限不足。Python 的zipfile在 Windows 下自动忽略了权限位,在 Linux 下会尝试保留,但有时也不可靠,稳妥起见可以在解压后手动设置:
import stat extracted_path.chmod(stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR | stat.S_IXGRP | stat.S_IRGRP)5.3 为什么不推荐 WebDriverManager
Java 生态里有现成的 WebDriverManager 库,Python 生态也有webdriver-manager包,一行代码就能自动下载驱动。那是不是直接用现成的库更省事?
我的观点是分情况。个人项目、快速验证脚本,用现成库完全没问题。但有几个原因导致我在生产环境更愿意维护自己的脚本:
- 依赖体积和更新风险:引入第三方库等于引入一层依赖,一旦库作者更新了接口或停止维护,整个自动化脚本可能受影响。
- 定制化需求:我们有些环境需要从内部镜像站下载驱动,而非访问外网,现成库不一定支持自定义下载源。
- 版本可控:自己写脚本,精确知道每次更新拉的是什么版本,出了问题排查链路清晰。第三方库封装了太多细节,报错时不方便定位。
当然,如果你的团队没有专门的工具链维护人员,用现成库是性价比最高的选择。只能说“够用”和“可控”各有取舍。
6. 常见问题与排查技巧实录
写这套脚本的过程中,我踩过不少坑,这里挑几个典型的案例分享,都是真实遇到过的问题。
6.1 接口返回 404:版本号格式对不上
第一次写get_chromedriver_version时,我直接把完整的浏览器版本号拼到 URL 里,结果部分版本会返回 404。查了下发现LATEST_RELEASE接口的版本参数有个规律:有的版本接受完整四位版本号,有的只接受前三位。稳妥的做法是统一截取前三位。
而且要注意,像105.0.5195这种版本,ChromeDriver 没有对应的发布(直接从 103 跳到 106 之类的发布节奏差异),接口会返回空内容或者直接 404。这时候需要做容错处理,打印明确的错误信息。
排查建议:先用浏览器手动访问一下构造的 URL,看看返回什么。404 就调整版本号位数,空内容就说明该版本没有驱动,要确认浏览器版本是否有问题。
6.2 下载的文件解压不了:网络中间层篡改或截断
有段时间脚本在测试环境频繁报“BadZipFile: File is not a zip file”,排查发现是公司网络出口的代理对下载内容做了缓存和改写,导致压缩包损坏。
解决办法:下载后先校验文件大小,对比Content-Length和实际写入的字节数,不一致就重试。如果重试后依然失败,加一个verify=False试一下(仅在确认环境安全的情况下),或者改用官方镜像源。
另外,下载时最好带一下User-Agent请求头,有些网关会拦截空 UA 的请求:
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"} resp = requests.get(url, headers=headers, timeout=60)6.3 驱动下载成功后 Selenium 还是报错
一种情况是驱动下载到了save_dir,但 Selenium 代码里还是用了老的路径。特别是 Windows 下,WebDriver 启动时会优先在当前环境变量PATH里找驱动。如果 PATH 里的老驱动排在前面,新下载的驱动根本不会被用到。
排查思路:在 Selenium 代码里显式指定service参数,把 executor_path 指向新下载的驱动路径,不要依赖系统 PATH。脚本输出里会有驱动路径,直接复制过去用就行。
另一种情况是驱动下载成功,但版本不匹配。比如浏览器是 114.0.5735.90,下载的驱动是 114.0.5735.16,理论上是兼容的——但在少数特定版本上,ChromeDriver 的 bug 会导致启动失败。这时候的排查方法是手动执行驱动看看报错内容:
chromedriver --version然后启动时带上--verbose参数查看详细日志。
6.4 多浏览器共存时的管理策略
一台机器上同时装了 Chrome 和 Edge,脚本不能只处理一个。我的习惯是在脚本里加一个映射表,统一管理所有浏览器的驱动目录:
BROWSER_DRIVERS = { "chrome": {"version_getter": get_chrome_version, "driver_getter": get_chromedriver_version, "driver_name": "chromedriver"}, "edge": {"version_getter": get_edge_version, "driver_getter": get_edgedriver_version, "driver_name": "msedgedriver"}, }以后要加浏览器,往这个表里加一项就行,主流程完全不用改。
6.5 离线环境怎么办
有安全管控的测试环境可能无法直接访问外网。这种场景下,可以在能联网的机器上先把驱动包下载好,放到一个内部共享目录或者镜像站。脚本里把下载地址替换成内部地址即可。
我实际用的是这样一个方案:主脚本保留访问官方接口的逻辑,同时允许传入一个base_url_override参数,指向内部镜像。镜像上保持和官方一致的文件结构:
{base_url}/{driver_version}/chromedriver_win64.zip这样脚本的逻辑基本不变,只是下载地址变了,维护成本很低。
7. 更进一步:把驱动更新集成到自动化测试框架里
脚本本身跑通,只能解决“手动下载”的问题。真正顺手的使用方式,是把驱动检查机制嵌到测试框架的初始化流程里。
一个比较实用的模式是:测试启动时,先调用ensure_driver(),如果驱动不存在或版本不匹配就自动更新,然后返回驱动路径供测试使用。这样测试同学在跑用例前不需要关心驱动版本,全自动处理。
@pytest.fixture(scope="session") def chrome_driver(): driver_path = ensure_driver(browser="chrome", save_dir=DRIVER_DIR) service = webdriver.ChromeService(executable_path=driver_path) driver = webdriver.Chrome(service=service) yield driver driver.quit()配合 CI/CD 流水线,还可以加一个定时任务,每周检查一次驱动版本是否有更新,必要时自动提交 PR 更新测试依赖。这样就把驱动维护从“遇到报错再处理”变成了“预防性维护”。
有一个细节想提醒大家:驱动目录不要放到临时目录。有些同学写了脚本测试,直接把驱动下到系统temp下,结果跑了一段时间后系统清理临时文件,驱动没了,测试全部报错。DRIVER_DIR 建议用固定的业务目录,比如/opt/drivers或者 Windows 下的D:\selenium-drivers,跟临时文件彻底隔离。
另外,多台测试机器批量部署时,可以把整个drivers目录打包分发,或者在每台机器上跑一次初始化脚本。相比之下,我更推荐后者——一次性初始化脚本比远程拷贝更灵活,能根据每台机器实际的浏览器版本拉取匹配的驱动,避免了手动维护版本清单的麻烦。
在我实际负责的测试环境里,最初每台机器驱动坏了都要手动处理,平均恢复时间差不多十五分钟。改成自动更新脚本后,处理故障变成了跑一条命令的事,几台机器十分钟内能全部搞定。如果涉及的环境数量更多,优势只会更明显。