☰
Python Selenium自动化:Chrome获取与ChromeDriver版本匹配指南
2026/10/20 7:49:18 网站建设 项目流程

每次看到 Selenium 自动化脚本报出session not created,我第一反应不是去翻定位逻辑,而是先看 Chrome 和 ChromeDriver 的版本是否匹配。这个习惯是被报错教育出来的。做 Python Selenium 自动化,第一步从来不是写find_element,也不是搭什么框架,而是把浏览器本体和驱动这对组合先弄对。尤其到了新电脑、新服务器、新容器里搭环境时,“如何获取适用于 Python Selenium 自动化的 Chrome 浏览器”这个问题几乎一定会出现。它看起来只是下载一个浏览器,里面却牵扯到版本匹配、安装路径、驱动协议、无头参数、并发目录等一系列细节。这篇文章就把这些细节一次性讲透。

1. 为什么说“获取Chrome”不仅仅是下载安装包

1.1 Selenium 不是直接打开你双击的那个 Chrome

很多人第一次接触 Selenium 时,以为它像按键精灵一样帮你点开一个现成的浏览器。实际上原理完全不同。Selenium 通过 WebDriver 协议向 ChromeDriver 发指令,ChromeDriver 再通过 DevTools 协议把指令翻译给 Chrome 浏览器执行。整个过程是一条完整的指令链:Selenium → ChromeDriver → Chrome。

这条链路里,ChromeDriver 必须是独立可执行文件,它不是浏览器的外挂插件,也不是 Selenium 库的一部分。很多人只装了浏览器,没装驱动,脚本报错后还在到处找定位问题;也有人在浏览器和驱动版本不匹配时,硬是去改 CSS 选择器,折腾半天发现毫无关系。等你理解了这条指令链,排查方向就清晰了:每一层都可能出问题,但高频问题几乎都集中在“浏览器找没找到”和“驱动版本对不对”两件事上。

你可以把这三者想象成遥控器和电视的关系。Selenium 是遥控器,Chrome 是电视机,ChromeDriver 是负责翻译红外信号的那块电路板。只买电视不装电路板,遥控器按了没反应;电路板型号不对,按了之后电视可能乱亮。下载浏览器只是“买电视”,但自动化能跑起来,靠的是完整链路。

1.2 版本错一个数字,整个会话起不来

ChromeDriver 和 Chrome 的版本匹配规则,用一句话概括就是:主版本号必须对齐。比如本机 Chrome 主版本是 120,那么 ChromeDriver 也最好选择 120 系列里的版本。不是要求小版本完全一模一样,但主版本一旦错开,基本启动即失败。

原因很简单:Chrome 每个大版本都可能调整内部实现、修改调试协议,旧驱动不认识新浏览器的握手信息,新驱动去控制老浏览器也可能触发各种诡异行为。于是你经常会看到这样的报错:

session not created: This version of ChromeDriver only supports Chrome version XXX

这个英文报错第一次遇见确实容易把人劝退,但处理方式其实非常机械:先确认浏览器真实版本号,再下载同主版本的 ChromeDriver。不要看到报错里写着“only supports Chrome version XXX”,就直接拿着 XXX 去搜索下载,那样很容易下到一个同样不匹配的版本,因为你的浏览器真实版本未必就是那个数字。

1.3 浏览器和驱动是两套独立的更新节奏

Chrome 稳定版通常几周就会一个大版本,ChromeDriver 也会跟着更新,但两者不是自动绑定的关系。Chrome 升级了,驱动不会跟着升;驱动升级了,Chrome 也不会自动匹配。

这带来一个很典型的局面:你本机 Chrome 开了自动更新,今天自动化任务还跑得好好的,明天早上突然全部报版本错误,甚至没有人动过任何代码。问题就出在浏览器的自动更新把版本悄悄推进了一个大版本,而 ChromeDriver 还停在旧版本。

所以在稳定的自动化项目里,我强烈建议手动控制浏览器版本,或者干脆使用下面会讲到的专门用于自动化的浏览器构建。自动化环境最怕“不稳定”,自动更新是最大的不稳定因子。

2. 适合 Selenium 自动化的 Chrome:三种下载方案横评

2.1 方案一:官方稳定版——适合日常调试和单机任务

先讲最常规的做法:在开发机上安装稳定版 Chrome,去浏览器官方下载页拿最新安装包,Windows 走安装向导,macOS 拖进应用程序目录,Linux 用系统包管理器安装。这种方式的优点是零学习成本,浏览器日常也能用,版本跟随官方更新,安全补丁来得快。

但有两个坑要注意。

第一,安装路径尽量别改。Selenium 在没有收到明确路径时,会按照操作系统的默认位置去找浏览器:Windows 通常检查 Program Files 目录,macOS 检查 Applications 目录,Linux 检查 PATH 里的 google-chrome。如果你把浏览器装到了自定义目录,脚本很可能直接报“找不到浏览器”。要么改回默认路径,要么用Options.binary_location明确告诉 Selenium 浏览器可执行文件在哪。

第二,如果用稳定版跑自动化,建议关掉自动更新。桌面版 Chrome 的自动更新机制会随时替换浏览器版本,而 Selenium 场景下我们希望环境可复现。自动更新一开,今天能跑的脚本明天可能就起不来。Windows 上可以通过更新策略配置、Linux 下可以锁住软件版本,本质上都是让浏览器版本不再自己乱跳。

如果是在没有外网的离线环境里部署,还可以提前下载好离线安装包,拷贝过去安装。离线包版本要记录清楚,随后拿同版本的 ChromeDriver 一起部署,这样比现场下载几百兆文件靠谱得多。

2.2 方案二:Chrome for Testing——适合测试矩阵、CI 和长期任务

如果想让自动化环境尽量“干净”,我推荐使用专门为测试和自动化设计的浏览器构建,通常叫 Chrome for Testing。这个构建不随稳定通道自动更新,版本号固定明确,解压即可运行,没有安装过程,也没有后台更新服务。配合同版本的 ChromeDriver,非常适合需要确定性的测试环境、跨版本浏览器矩阵和 CI 流水线。

获取方式也很简单:进入官方公开的下载页面,按系统平台和所需版本号选择对应压缩包,下载后解压,找到其中的 chrome 可执行文件即可。这类构建的文件名通常直接带版本号,所以你很容易确认现在用的到底是哪个版本。

这里必须提醒一句:市面上很多第三方“绿色版”“精简版”浏览器,来源不可控,可能被塞进私货,也可能对 Chromium 的编译选项做奇怪改动,导致 Selenium 行为异常。你追求自动化稳定,就不该用这种来路不明的包。官方提供的 Chrome for Testing 已经解决可控性问题,没必要贪图小体积去冒风险。

使用 Chrome for Testing 时,团队项目最好把浏览器版本和驱动版本写进一个配置文件,升级时先跑冒烟脚本验证,再全量替换节点。因为它不自动更新,所以安全补丁也需要你主动跟进,每隔一段时间去官方页面确认有没有新版,然后在可控环境里统一升级。

2.3 方案三:Chromium 或系统包自带浏览器——轻量但版本难控

除了 Chrome 本体,很多人也会在 Linux 容器里直接用系统包管理器安装 Chromium。比如一行apt install chromium就能拿到一个可以直接运行的浏览器。优点是安装简单、镜像体积小,缺点也很明显:系统源里的 Chromium 版本往往滞后于 Chrome 稳定版,而且版本号体系和官方 Chrome 不完全一致,对应 ChromeDriver 的选择会比较麻烦。

如果你的任务只是做简单的页面抓取、截图,不依赖特别新的浏览器特性,Chromium 也能凑合用。可一旦遇到协议层面不兼容的问题,或者需要精确复现某个浏览器版本的行为,还是要回到官方构建上。我在容器环境里更倾向于直接用 Chrome for Testing,因为它的版本与 ChromeDriver 是严格绑定的,踩坑最少。

2.4 自动匹配驱动的工具,帮不了“浏览器本体缺失”

从 Selenium 4.6 开始,Selenium 自带了一个叫 Selenium Manager 的组件。你不写驱动路径时,它会自动去系统里找 Chrome,自动下载匹配的 ChromeDriver,很多本机脚本因此变得非常简洁。比如直接webdriver.Chrome(),新版 Selenium 就会尝试完成驱动匹配。

但 Selenium Manager 的职责更多在“驱动管理”,它解决的是“驱动版本匹配”问题。如果系统里根本没有 Chrome,它不一定能帮你凭空变出一个合适的浏览器。所以我的建议是:浏览器本体还是自己准备,自动匹配的职责交给驱动层就好。

另一个常见选择是第三方库webdriver-manager,它可以根据本机浏览器主版本自动下载对应 ChromeDriver 到本地缓存,后续启动不再重复下载。安装和执行都很简单:

pip install webdriver-manager
from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager service = Service(ChromeDriverManager().install()) driver = webdriver.Chrome(service=service)

这种方式很适合本机调试,省掉了手动查版本号、手动下载驱动的时间。但要注意,多个进程同时调用这个库时,可能会一起写同一个缓存文件,出现资源竞争。在 CI 容器里,我更推荐把驱动预置进镜像,而不是运行时临时下载。

3. 从零搭建环境:看完这一章就能跑通脚本

3.1 环境清单与目录规划

在动手之前,建议先把目录结构固定下来,不要在脚本里写各种魔鬼路径。比如这样:

D:\auto_env\ ├─ chrome\ │ └─ chrome.exe ├─ chromedriver\ │ └─ chromedriver.exe └─ test_01.py

机器上需要预装 Python 3.10 或以上版本,创建虚拟环境后安装 selenium:

python -m venv venv venv\Scripts\activate pip install selenium

浏览器和驱动建议手动放置。浏览器用 Chrome for Testing 或者官方稳定版都可以;驱动在 ChromeDriver 官方下载页找到与浏览器主版本一致的版本。很多人以为驱动一定要复制到 Python 目录或者加入 PATH 才能用,其实完全没必要,只要在脚本里用 Service 指向驱动文件就行。保持目录整洁,对后续维护是很大的帮助。

3.2 最小脚本:显式指定浏览器和驱动路径

下面这段脚本,我每次搭新环境都会先跑一遍,用来确认基础链路通畅:

from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.service import Service options = Options() options.binary_location = r"D:\auto_env\chrome\chrome.exe" service = Service(r"D:\auto_env\chromedriver\chromedriver.exe") driver = webdriver.Chrome(options=options, service=service) driver.get("https://example.com") print(driver.title) driver.quit()

这里有三个关键点。

options.binary_location必须指向浏览器可执行文件的精确位置,注意是 chrome.exe 或 google-chrome,而不是所在的目录。如果路径中有反斜杠,最好在字符串前加r做成原始字符串,避免\c之类被识别成转义字符。

Service用来指定 ChromeDriver 路径。Selenium 启动时会自动拉起这个进程,你不必手动运行驱动,也不要在脚本里另起线程去管它。

driver.get(...)会打开指定网页,driver.title返回页面标题。如果运行后正确打印出了标题,说明浏览器、驱动、Selenium 三方已经打通。如果这一步就报错,直接去看下一章的问题排查。

3.3 版本信息怎么看,不要靠猜

不知道版本号匹配,就等于在盲跑。你可以先用操作系统命令直接查看驱动版本:

import subprocess result = subprocess.check_output([r"D:\auto_env\chromedriver\chromedriver.exe", "--version"]) print(result.decode())

启动后的会话能力里也能读取浏览器和驱动的真实版本:

print(driver.capabilities.get("browserVersion")) print(driver.capabilities.get("chrome")["chromedriverVersion"])

我建议在自动化项目启动日志里固定加这两行。每次出现“刚才还能跑现在不能跑”的怪问题时,先看这两个数字是不是已经从 120.0.x 变成了 121.0.x。版本问题的定位效率,全靠这个习惯撑起来的。

3.4 无头模式:服务器上没有显示器怎么跑

如果你要在 Linux 服务器或 CI 容器里跑,没有图形界面,可以改用无头模式。核心参数如下:

options.add_argument("--headless=new") options.add_argument("--no-sandbox") options.add_argument("--disable-dev-shm-usage") options.add_argument("--disable-gpu")

各个参数的作用需要解释清楚,不要无脑复制。

--headless=new是新版 Chrome 支持的无头模式,老代码里常见的--headless虽然也能用,但新版会提示参数已废弃。如果你的任务需要完整渲染页面,建议用新的无头模式。

--no-sandbox在 Docker 容器里以 root 用户运行时基本是必加的,否则 Chrome 拒绝启动。开发机上不需要加,加了反而降低安全性。

--disable-dev-shm-usage解决的是容器/dev/shm空间太小导致页面崩溃的问题。加了之后 Chrome 会改用 /tmp 存放共享内存文件,稳定性提升非常明显,尤其是在跑多个并发实例时。

--disable-gpu不是必须的,但无头环境下 GPU 渲染本来就没意义,加上能少一些无用报错。

无头环境跑通之后,可以把同一套东西打包进镜像,只要保持浏览器路径固定、驱动版本固定,换任何一台机器行为都一样。

3.5 常用启动参数速查

参数用途什么时候加
--headless=new无头模式运行服务器、CI、没有显示器的环境
--no-sandbox关闭浏览器沙箱容器内以 root 运行时
--disable-dev-shm-usage绕过共享内存过小问题Docker、并发实例多时
--disable-gpu关闭 GPU 加速无头环境、虚拟机
--user-data-dir=路径指定独立用户数据目录多实例并发时必须用

参数不是加得越多越好,理解每个参数背后的原因,才能在你真的踩坑时知道改哪里。

4. 版本、路径、沙箱和下载目录:四个高频报错一次讲透

4.1 session not created:驱动和浏览器版本错配

这个报错非常经典,常见长这样:

session not created: This version of ChromeDriver only supports Chrome version XXX

处理方法很简单:下载与浏览器主版本一致的 ChromeDriver;或者把浏览器降到驱动支持的版本;或者干脆使用自动匹配工具。但这里往往藏着一个更隐蔽的问题——机器上是不是装了多个 Chrome。比如系统里既有稳定版,又有测试版,你给 Selenium 指定的驱动是最新版,它自动找到的却是旧版浏览器。

所以最稳妥的解决办法是:始终在 Options 里显式设置binary_location,不要让 Selenium 去猜。猜的结果通常都是浪费时间。

4.2 cannot find Chrome binary:路径没告诉对

另一个高频报错:

unknown error: cannot find Chrome binary

意思是 ChromeDriver 找到了,但浏览器可执行文件没找到。常见原因包括:浏览器没装、装在自定义目录、容器镜像里没有 Chrome、或者运行账号对浏览器目录没有读取权限。

解决办法是先在系统里确认真实路径,再在代码里配置:

options.binary_location = "/opt/chrome-linux64/chrome-linux64/chrome"

Linux 下可以用which google-chrome或which chromium查找;Windows 下确认 Program Files 目录里的实际文件名。还有一种少见情况:机器上明明有浏览器,但脚本跑在服务账号下,服务账号看不到当前用户的 GUI 应用目录,也会出现类似报错。这种需要检查运行账号的权限,而不是单纯补路径。

4.3 Running as root without --no-sandbox:容器里的老朋友

容器里以 root 运行 Selenium 时,最典型的报错是:

Running as root without --no-sandbox is not supported.

原因在于 Chrome 的沙箱机制在 root 下无法正常开启。加上--no-sandbox后问题就会消失,这也是容器版 Selenium 镜像里的通用做法。但要提醒一句:关闭沙箱意味着浏览器对恶意页面的隔离能力下降。如果你抓取的站点不可信,尽量把任务放在独立网络环境里运行。

与之相伴的另一个高频问题叫DevToolsActivePort file doesn't exist。这通常不是端口占用的错,而是 Chrome 进程根本没有成功启动。常见诱因是共享内存不足、磁盘空间不够、并发实例太多。加--disable-dev-shm-usage,清理磁盘,限制并发,都能缓解。

4.4 无头模式下载文件:找不到文件是目录问题

无头模式下,Chrome 的下载行为跟有头模式不太一样。你不设置下载目录时,文件可能被丢进临时目录,任务一结束目录就清理;或者你点击下载按钮之后,很难立刻知道文件去了哪里。

解决方式是预先在 Options 里指定下载目录:

import os download_dir = os.path.join(os.getcwd(), "downloads") os.makedirs(download_dir, exist_ok=True) prefs = { "download.default_directory": download_dir, "download.prompt_for_download": False, "safebrowsing.enabled": True, } options.add_experimental_option("prefs", prefs)

download.default_directory设定文件保存位置,prompt_for_download关闭下载确认框,safebrowsing.enabled避免部分文件被安全浏览拦截。配置之后,每次跑完任务去指定目录找文件,就不会出现“明明点了下载,什么都找不到”的情况。

4.5 报错速查表

报错关键词常见原因优先处理方向
session not created ... supports Chrome version驱动与浏览器版本不一致按主版本下载驱动,显式指定 binary_location
cannot find Chrome binary没装浏览器或路径未指定安装浏览器,并设置 binary_location
Running as root without --no-sandboxroot 环境执行加 --no-sandbox
DevToolsActivePort file doesn't exist浏览器没起来加 --no-sandbox、--disable-dev-shm-usage,检查资源和磁盘
unknown error: session deleted because of page crash浏览器崩溃降低并发,检查内存,考虑独立 user-data-dir

这张表不是让你背,而是遇到问题时有地方可查。

5. 本机、CI、并发采集三场景下的浏览器选型策略

5.1 本机调试:怎么省心怎么来

如果你只是写脚本、搞爬虫、做小批量验证,浏览器就用日常稳定版,驱动交给 Selenium Manager 或 webdriver-manager 自动匹配。这样版本跟不上、驱动找不到的问题会少很多。

但请记住一点:在线调通的脚本,如果要长期跑,最好把版本钉死。至少记录下浏览器主版本和驱动版本,否则某天系统提示 Chrome 升级后,你的脚本可能在半夜悄悄报废。随手把版本号写进项目的 README 或启动配置,成本很低,但能省掉非常多排查时间。

5.2 CI 容器:固定版本加预置缓存

在 CI 流水线里,我不建议运行时下载浏览器或驱动。因为每次构建都拉一次网络,第三方源一抖动,构建就挂;而且容器镜像一旦包含自动更新组件,构建就不可复现。

我的做法是把 Chrome for Testing 和对应 ChromeDriver 直接打包进镜像,用环境变量控制版本号:

CHROME_VERSION=120.0.6099.109 CHROMEDRIVER_VERSION=120.0.6099.109

构建脚本按这两个变量从内部缓存拉取对应软件包,复制到固定路径,然后运行一个最简单的冒烟脚本。下次 Chrome 需要升级时,只改版本变量,代码和镜像其余部分完全不动。这样同时满足了可复现和易升级两个需求。

5.3 大规模采集:每个 worker 要有独立 profile 目录

很多人用 Selenium 做并行采集时,习惯在多进程里各自创建webdriver.Chrome(),结果经常报错或者页面串扰。最常见的原因就是多个 Chrome 实例共用了同一个user-data-dir。

Chrome 对用户数据目录有锁。多个实例共用同一个目录时,第二个实例会试图把请求转给第一个已有进程,驱动会话自然就乱掉了。正确做法是给每个 worker 分配独立目录:

options.add_argument(f"--user-data-dir=C:\\tmp\\worker_{worker_id}")

浏览器本体可以共享同一个安装目录,但用户数据目录必须各自独立。同时,并发数量不要直接拉满,要参考机器的 CPU 和内存。每个 Chrome 实例大概占几百 MB 内存,开 20 个 worker 的容器通常建议预留 8GB 以上。

5.4 版本升级的标准化流程

版本升级是自动化项目里最容易翻车的操作。我现在要求团队走固定四步:先在开发机装新版本浏览器和新版本驱动,跑 10 行冒烟脚本;确认主版本、标题输出、截图生成都正常;然后把这些版本号更新到 CI 配置;最后在容器里重新构建镜像,再跑一遍完整回归。

这套流程看起来很笨,但确实能避免大多数“升完级脚本全挂”的事故。凡是依赖外部组件的自动化项目,都不建议直接在生产点把浏览器升级到最新版,再让代码去适应它。把环境变化纳入变更管理,才是真正省事的方式。

我个人现在的固定套路是:本机调试用稳定版加自动驱动匹配;线上和 CI 只认 Chrome for Testing 固定版本,并把版本号作为环境变量传进构建流程;每次升级前后先跑一个 10 行的冒烟脚本,打印 browserVersion 和 chromedriverVersion,确认一致再继续。这套流程不复杂,但足够让“找浏览器”这件事从玄学回归到常识。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询