从一次驱动失配说起版本没对上Edge就是打不开搞爬虫的朋友基本都绕不开 Selenium而 Selenium 自动化 Edge 浏览器的一大前提是本地必须有一个和浏览器主版本严格对应的 msedgedriver。以前我刚开始接触这块时常常天真地以为只要装了个 Selenium 就能直接启动 Edge结果第一次运行就收到一条让人摸不着头脑的报错selenium.common.exceptions.WebDriverException: Message: session not created: This version of MSEdgeDriver only supports MSEdge version 109 Current browser version is 122.0.2365.66 with翻译过来就是当前这个 msedgedriver 只支持 Edge 109而浏览器本体已经是 122。这种报错没有任何歧义就是版本对不上。更让人头疼的是Edge 本身就自带自动更新机制可能上午还好好的下午系统静默推送了一个版本更新你的驱动又废了。今天这篇就专门讲清楚一件事怎么彻底解决“浏览器版本号与 msedgedriver 版本号不匹配”这个老问题——核心思路就是“先拿到浏览器真实版本号再去官网自动下载匹配的驱动”整个过程可以全自动完成。这篇文章适合以下几类人刚刚开始学网络爬虫、第一次用 Selenium 控制 Edge 的新手本地经常出现驱动失配报错、每次都要手工下载驱动的老手以及打算把爬虫脚本部署到服务器、希望启动前自动化完成环境自检的朋友。1. 为什么会失配Edge 自动更新与驱动手工下载之间的矛盾1.1 浏览器和驱动的版本对应规则是什么先明确一个基本概念msedgedriver是官方提供的 WebDriver 实现Selenium 通过它来和 Edge 的自动化测试端口通信。因为不同浏览器版本对 WebDriver 协议的支持程度有差异所以官方约定驱动的主版本号必须与浏览器的主版本号一致。这里是“主版本号一致”写成数字就是大版本首段相同。比如浏览器是 122.0.2365.66驱动就应该是 122.x.x.x浏览器是 109.xxx驱动就只能是 109.xxx不能用 108也不能用 110。这和 ChromeDriver 的规则一致。你要是把版本关系想象成一把钥匙配一把锁钥匙头部的齿纹形状决定它是否插得进锁孔驱动主版本就相当于“齿纹雏形”浏览器的主版本对应“锁孔”。齿形一致才能进一步磨合次版本号的细节差异。1.2 为什么“手工下载一次”不够用很多新手第一次顺利跑通后会有一个错觉驱动已经下载好了以后就不用管了。但 Edge 的自动更新策略相当激进如果你的系统开启了自动更新Edge 会不定期静默升级。举个典型的场景你本周下载了 Edge 114 对应的 msedgedriver隔两周再打开浏览器查看版本号可能会发现已经变成 116。此时你本地那份 114 的驱动还没有任何变化程序一跑必定报错。我个人的建议是不要试图关闭 Edge 的自动更新来换取“驱动不用常换”——因为你关闭了自动更新后浏览器版本停在了旧版本指纹老旧碰到新版网站的脚本逻辑容易出兼容问题。更好的办法是让爬虫程序在启动时“自己动手”校验版本如果本机没有驱动或者版本不对就立刻去官网下载匹配版本。这样无论浏览器被更新到哪一个版本脚本永远能自适应。2. 拿到 Edge 真实版本号的三种方案既然要自动匹配第一步一定是读出版本号。这里我们要区分一个细节用户平时在 Edge 的“设置-关于”里看到的版本号和命令行里查出来的 Should 是一致的但在写自动脚本时我们要选择稳定可靠、容易被程序解析的输出方式。下面是三种常用方法我按使用场景列个表对比方案获取方式优点缺点推荐度命令行取值reg query查询注册表输出简洁、稳定需要解析字符串非常推荐注册表直接读Pythonwinreg模块原生访问速度快代码稍微繁琐非常推荐浏览器页面手动看访问edge://version/可视化最直观不适合自动化仅人工排错用2.1 命令行取值一次 reg query 就搞定Windows 下最简单的方式是直接读取 Edge 的注册表项。打开命令提示符执行reg query HKEY_CURRENT_USER\Software\Microsoft\Edge\BLBeacon /v version输出长这样HKEY_CURRENT_USER\Software\Microsoft\Edge\BLBeacon version REG_SZ 122.0.2365.66那个122.0.2365.66就是我们要的东西。这条命令的路径BLBeacon是 Edge 用来记录版本信息和更新状态的地方即使浏览器正在运行也能读到。2.2 Python 脚本里直接读注册表如果你希望整个流程都在 Python 里完成不借助 subprocess 去调系统命令可以用winreg模块import winreg def get_edge_version(): key_path rSoftware\Microsoft\Edge\BLBeacon try: key winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path) version, _ winreg.QueryValueEx(key, version) winreg.CloseKey(key) return version except FileNotFoundError: return None print(get_edge_version()) # 例如122.0.2365.66这里要注意在团队协作或不同环境的电脑上有些精简版系统可能没有BLBeacon这个路径那就会抛出FileNotFoundError。作为兜底方案我会再拼一个HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge之类的路径去查但多数情况下HKEY_CURRENT_USER就足够。2.3 页面兜底edge://version/ 的用处如果某一天命令和注册表全失效了你还可以手动打开 Edge在地址栏输入edge://version/这里能看到完整版本号、用户数据目录、命令行参数等所有信息。对自动化脚本来说这个页面不好直接解析但是作为人工排错非常有价值。遇到奇怪问题比如驱动启动后报“无法加载扩展”或“无法定位浏览器二进制文件”先在这里确认浏览器版本能帮你快速定位问题是否出在根本版本上。3. 从版本号到驱动下载官网 URL 的规律拿到版本号之后下一个核心问题就是去哪里下载对应版本的 msedgedriver如果每次都去搜索引擎找网站那可太慢了而且第三方下载站经常捆绑东西安全性也堪忧。正确的做法是直接走微软官方渠道而且官方下载地址的规律非常固定。3.1 微软官方驱动的下载地址规律微软为 msedgedriver 提供了稳定的 CDN 路径格式如下https://msedgedriver.azureedge.net/{完整版本号}/edgedriver_win64.zip把{完整版本号}替换成第 1 步拿到的浏览器版本号就可以直接下载。比如浏览器版本号是122.0.2365.66下载地址就是https://msedgedriver.azureedge.net/122.0.2365.66/edgedriver_win64.zip解压出来会得到msedgedriver.exe。注意64 位系统和 32 位系统的文件名不同常见的有平台ZIP 文件名Windows 64 位edgedriver_win64.zipWindows 32 位edgedriver_win32.zipmacOSedgedriver_mac64.zipLinuxedgedriver_linux64.zip绝大多数现代个人电脑都是 64 位系统直接取edgedriver_win64.zip就好。服务器如果是 32 位系统才考虑win32版本。3.2 一定要用“完整版本号”去拼 URL这里是个很容易踩的细节官方 CDN 路径要求的是完整版本号比如122.0.2365.66而不是只取主版本号122。如果你把 URL 拼成https://msedgedriver.azureedge.net/122/edgedriver_win64.zip那大概率会得到一个 404 页面。原因在于微软的发布目录是以具体构建版本为维度去组织的同为大版本 122 的版本可能有122.0.2365.59、122.0.2365.66等多个构建它们各自对应一个独立的文件夹。所以第 1 步拿到的完整版本号必须一个点不漏地拼进 URL。3.3 如果浏览器的版本号在官网找不到怎么办这种情况偶尔会出现尤其是急着用新功能把 Edge 升级到了 Dev 或 Beta 渠道的老哥们。比如浏览器版本号是130.0.2849.5结果去官方 CDN 上访问对应 URL 却 404 了。这种时候不要慌优先尝试降级方案把版本号末段试着减一减比如130.0.2849.5尝试130.0.2849.4看能不能命中。如果还是不行去官方发布历史页面查一下同主版本下已发布的稳定版本列表选一个主版本一致、微版本尽量接近的驱动来用。从实测来看只要主版本一致微版本相差几位通常是可以兼容的。比如浏览器是122.0.2365.70用122.0.2365.66的驱动也能正常启动但反过来如果用121主版本驱动去带122浏览器则必然失败。所以我们的策略是优先精确匹配匹配不上再退而求其次找同主版本最近的构建。4. 自动检测与下载驱动的完整代码实现接下来就是完整的实践环节。我写了一个工具函数整体思路是读注册表版本号 - 拼 URL - 下载 ZIP - 解压到本地目录 - 返回驱动路径。这个函数可以放到任何爬虫脚本开头每次启动前自动执行达到“环境自愈”的效果。4.1 先设计函数结构这个函数要满足几个关键需求支持传一个driver_dir指定驱动存放目录方便统一管理。下载前先判断本地已有的 msedgedriver.exe 是否已是最新版本如果已经匹配则跳过下载省时间也省流量。返回驱动文件的绝对路径方便后面传给 Selenium 的webdriver.Edge使用。我先直接给出完整的 Python 代码import os import re import ssl import urllib.request import urllib.error import winreg import zipfile import shutil def get_edge_version(): 读取 Edge 浏览器完整版本号 key_path rSoftware\Microsoft\Edge\BLBeacon try: key winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path) version, _ winreg.QueryValueEx(key, version) winreg.CloseKey(key) return version.strip() except FileNotFoundError: # 兜底尝试读系统级注册表 try: key winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_path) version, _ winreg.QueryValueEx(key, version) winreg.CloseKey(key) return version.strip() except FileNotFoundError: return None def get_driver_dir(base_dirNone): 确定驱动保存目录 if base_dir is None: base_dir os.path.join(os.path.expanduser(~), .msedgedriver) os.makedirs(base_dir, exist_okTrue) return base_dir def is_driver_matched(driver_path, browser_version): 检查现有驱动是否与浏览器版本匹配 if not os.path.exists(driver_path): return False major_browser browser_version.split(.)[0] # 尝试读取驱动文件版本信息Windows 下可用 GetFileVersionInfo这里简化处理 try: info __import__(subprocess).check_output( [powershell, -Command, f(Get-Item {driver_path}).VersionInfo.FileVersion], shellTrue ).decode(utf-8).strip() driver_major info.split(.)[0] return driver_major major_browser except Exception: return False def download_driver(browser_version, base_dirNone): 下载并解压对应版本的 msedgedriver driver_dir get_driver_dir(base_dir) driver_path os.path.join(driver_dir, msedgedriver.exe) # 如果已经存在且版本匹配则直接返回 if is_driver_matched(driver_path, browser_version): return driver_path # 拼官方 CDN 地址 url fhttps://msedgedriver.azureedge.net/{browser_version}/edgedriver_win64.zip zip_path os.path.join(driver_dir, edgedriver_win64.zip) # 下载 print(f[*] 正在下载驱动: {url}) ssl_ctx ssl.create_default_context() try: with urllib.request.urlopen(url, contextssl_ctx, timeout30) as resp: with open(zip_path, wb) as f: shutil.copyfileobj(resp, f) except urllib.error.HTTPError as e: if e.code 404: raise RuntimeError(f官网不存在该版本: {browser_version}请手动检查版本) from e raise # 解压 print(f[*] 正在解压到: {driver_dir}) with zipfile.ZipFile(zip_path, r) as zf: zf.extractall(driver_dir) # 清理 zip os.remove(zip_path) return driver_path def init_edge_driver(base_dirNone): 初始化驱动返回驱动路径 browser_version get_edge_version() if browser_version is None: raise RuntimeError(无法读取 Edge 浏览器版本号请确认浏览器已安装) driver_path download_driver(browser_version, base_dir) return driver_path, browser_version4.2 对下载块的 UAC 和网络细节解释脚本里有一个容易被忽略的地方我每次下载前用ssl.create_default_context()创建了默认 SSL 上下文。为什么特意写这一行因为个别网络环境下urllib.request.urlopen默认 SSL 校验可能会因为系统证书链不完整而报URLError: urlopen error [SSL: CERTIFICATE_VERIFY_FAILED]。显式创建上下文并走默认证书链能够最大程度地兼容常见 Windows 环境。当然如果你们公司内网有 HTTPS 劫持或特殊证书可能需要引入certifi包并把上下文替换成ssl.create_default_context(cafilecertifi.where())这个看具体场景。另外下载超时我设成了 30 秒。如果网速很慢这个时间可能需要调大一点否则下载过程刚好卡在某个时间点会误报网络异常。4.3 Selenium 启动 Edge 时的对接写法驱动是下载好了但怎么交给 Selenium如果你用的是 Selenium 4.x 以上的版本推荐用Service对象来指定驱动路径from selenium import webdriver from selenium.webdriver.edge.service import Service driver_path, browser_version init_edge_driver() options webdriver.EdgeOptions() options.add_argument(--start-maximized) # 如果需要无头模式取消下一行注释 # options.add_argument(--headlessnew) service Service(driver_path) driver webdriver.Edge(serviceservice, optionsoptions) driver.get(https://www.baidu.com) print(driver.title) driver.quit()早期的写法是用webdriver.Edge(executable_pathdriver_path)这个参数在 Selenium 4 里已经被标记为弃用建议统一使用Service因为Service还额外支持设置日志路径、端口等参数。后续如果驱动启动失败想查看底层日志可以通过serviceService(driver_path, log_outputos.path.devnull)或指定一个 log 文件路径来看更详细的原因。4.4 首次运行效果演示假设本机 Edge 版本是122.0.2365.66驱动目录还没有任何文件控制台输出大致是这样[*] 正在下载驱动: https://msedgedriver.azureedge.net/122.0.2365.66/edgedriver_win64.zip [*] 正在解压到: C:\Users\YourName\.msedgedriver第二次运行时因为驱动已经存在且主版本匹配输出变为驱动已存在且版本匹配跳过下载这句话在is_driver_matched返回True时由download_driver内部打出来。如果你也想看到这个提示可以在download_driver里加一个print([] 驱动已存在且版本匹配跳过下载)这样你就很清楚程序有没有走“无脑下载”的分支避免每次启动都浪费时间去访问 CDN。5. 驱动与浏览器主版本一致还不够细说第二次启动时的隐藏问题在主版本一致的前提下爬虫基本能跑起来。但真正干活的时候还有几个隐藏问题会导致 Edge 打不开、闪退或无法建立连接。下面这几个场景是我在多次实战中遇到的每一个都值得单独注意。5.1 msedgedriver 被系统当作“未知程序”拦截Windows Defender 或第三方杀软有时候会把msedgedriver.exe当成可疑程序直接隔离特别是你自己写脚本自动下载、解压并放入用户目录的场景。这种隔离很隐蔽脚本不会报语法错误但 Selenium 启动时突然提示selenium.common.exceptions.WebDriverException: Message: unknown error: cannot find Chrome binary或者干脆 driver 进程闪退连具体原因都没有。解决思路有两个将驱动目录加入杀软的信任白名单首选方案每次启动前先检测驱动文件是否存在如果不存在立刻重新下载并替换。我在脚本里已经通过is_driver_matched对“驱动是否存在”做了前置判断如果被删除或隔离下次启动时会自动重新下载所以从这个角度讲自动下载方案也算有“自愈能力”。5.2 路径里的反斜杠和中文目录一个很经典的坑Windows 绝对路径是C:\Users\张三\.msedgedriver\msedgedriver.exe在服务或者传给默认参数时如果路径里有反斜杠且没有正确转义Selenium 会报找不到文件或无法执行程序。这种问题尤其容易出现在使用os.path.join拼路径随后直接塞给Service的场景中——因为在某些底层封装里单个反斜杠会被当作转义字符处理。稳妥的做法是拿到路径后统一转换成正斜杠。driver_path driver_path.replace(\\, /) service Service(driver_path)顺便提一句如果用户名是中文或者驱动目录的上级目录带了空格比如Program Files (x86)也容易触发一些奇奇怪怪的问题。最简单粗暴的规避方式把驱动目录固定在一个纯英文、没有空格的路径下比如C:\SeleniumDrivers\EdgeDriver。上面的代码里我默认放到用户目录~/.msedgedriver如果在公司电脑上遇到权限问题建议改到C:\EdgeDriver这类根目录下。5.3 无头模式启动失败的两种表现服务器上跑爬虫通常要开无头模式headless让 Edge 在后台运行不弹出窗口。新版 Edge 在无头模式下对驱动版本更加敏感如果驱动版本和浏览器版本之间有细微差异可能出现启动后没有报错但driver.get()卡住数分钟甚至超时页面空白、截图全黑。遇到这种情况我的排查顺序是先去掉--headlessnew参数改成有头模式跑一遍确认脚本本身没问题再确认驱动版本是否精确匹配最后如果还不行尝试给 Edge 加上--disable-gpu和--no-sandbox参数尤其是跑在 Windows Server 或虚拟机里的时候这两个参数能避开不少显卡驱动和权限相关的问题。options webdriver.EdgeOptions() options.add_argument(--headlessnew) options.add_argument(--disable-gpu) options.add_argument(--no-sandbox)5.4 一个容易被忽视的耗时点首次启动驱动时的端口占用Selenium 启动 Edge 时msedgedriver 会监听一个本地端口默认是 9515。如果你先前开发的脚本进程没被干净地关闭比如异常退出、断点调试停在半路这个端口可能仍然被残留进程占用。新驱动启动时会报“端口已被占用”或者直接连接失败。处理方式也很简单在 Selenium 4.x 的Service里指定一个空闲端口或者启动前把残留的msedgedriver.exe进程清掉。我更推荐后者因为顺手可以释放内存import subprocess subprocess.run([taskkill, /f, /im, msedgedriver.exe], capture_outputTrue)注意这条命令会把所有msedgedriver进程全部结束如果有多个爬虫在并发跑那要先评估一下影响不能无脑执行。并发场景下应该用Service(xxx, port随机空闲端口)的方式规避端口冲突。6. 服务器部署与定时任务自动检测脚本的正确使用姿势爬虫写好后下一步通常是部署到服务器或加入计划任务。部署环境下驱动自动匹配的价值体现得更明显——因为你不会随时手动登录服务器去更新驱动完全靠计划任务在凌晨自动跑。6.1 把自动检测放到启动入口结构上我一般会写一个entry.py内容大致是# entry.py from driver_utils import init_edge_driver from selenium import webdriver from selenium.webdriver.edge.service import Service def main(): driver_path, _ init_edge_driver() service Service(driver_path) driver webdriver.Edge(serviceservice) # 业务逻辑 driver.get(https://target-site.com) ... driver.quit() if __name__ __main__: main()这样每次计划任务执行时都会先调用init_edge_driver使驱动与浏览器版本保持一致。加一个--log-levelWARNING之类的参数还可以减少控制台噪音。6.2 Windows 计划任务里必须注意的权限差异如果计划任务以 SYSTEM 或管理员身份运行浏览器所读取的注册表路径可能会和普通用户登录时不一样。因为注册表的HKEY_CURRENT_USER其实指的是“当前用户的注册表”SYSTEM 用户和你的业务账号不是同一个会话所以可能读到None。这个问题的常见表现是手动运行 entry.py 一切正常计划任务一跑就报“无法读取 Edge 浏览器版本号”。解决方案是在get_edge_version里把兜底路径也加上或者让计划任务直接用你的登录账号运行并勾选“仅在用户登录时运行”。从我的实际经验来看后者的可信度更高因为 Selenium 自动化需要用户态的图形环境以真正的用户身份跑最稳妥。6.3 定时任务里的网络波动影响自动下载依赖公网访问微软官方 CDN如果计划任务执行时间正好赶在网络波动期下载可能会失败。所以download_driver里最好加重试机制。一个简单可用的重试代码如下import time MAX_RETRY 3 for attempt in range(1, MAX_RETRY 1): try: with urllib.request.urlopen(url, contextssl_ctx, timeout30) as resp: with open(zip_path, wb) as f: shutil.copyfileobj(resp, f) break except Exception as e: print(f[!] 第 {attempt} 次下载失败: {e}) if attempt MAX_RETRY: raise time.sleep(3 * attempt)这样即使第一次下载失败后面两次重试大概率能成功夜班计划任务的健壮性会好很多。7. 版本匹配只是起点驱动更新的完整策略自动下载驱动解决了“启动前环境自愈”的大问题但实际长期跑爬虫的人都知道版本匹配放在整个爬虫工程里只是第一步。真正影响稳定性的还有“目标网站的反爬策略”“session 复用”“cookies 持久化”“代理 IP 池”等不过这些和本主题关系不大这里不多展开。从我个人的体会来说版本自动匹配最大的价值在于它把环境问题从“人肉排查”变成“程序自愈”。你不再需要为浏览器更新而后知后觉不再需要每天担心驱动失配报警。把自动检测的逻辑放进爬虫工程的启动流程里相当于每次运行都自动完成一次环境体检。这是一件一劳永逸的事情值得在项目初始阶段就设计进去。最后分享一个我在实际踩坑后总结的小细节解压驱动时微软的 zip 包里其实包含msedgedriver.exe和一个LICENSE文件如果你用解压库把整个文件夹一次性解压到当前目录可能会在项目目录里留下多余文件。最好指定一个专门的驱动目录比如~/.msedgedriver/让所有临时文件留在那里避免把项目目录弄得乱七八糟。另外驱动下载好之后建议给驱动加一个只读属性Windows 下可用attrib R msedgedriver.exe防止杀毒软件或某些脚本误删改这个关键文件这一点是我在一台服务器上屡次遇到驱动“神秘消失”之后才发现的。希望这篇文章能帮你把 Edge 驱动这个问题彻底解决少走我之前走过的弯路。
阅读完成 · 觉得有帮助?