
搞Web自动化测试的人应该都遇到过这一幕脚本写了半天逻辑没毛病一执行就报SessionNotCreatedException仔细一看是因为驱动版本和浏览器版本对不上。要是你正好在用Edge浏览器跑Selenium那么“EdgeDriver”就是绕不过去的一环。我最早从ChromeDriver转到EdgeDriver的时候也以为就是换个驱动文件的事结果踩了不少坑。今天这篇我就把Selenium和EdgeDriver这套东西从头到尾拆一遍包括驱动到底怎么选、版本怎么匹配、代码怎么写、元素怎么定位、常见报错怎么排查。不管你是刚入门的测试新人还是想从Chrome切到Edge的老手这篇文章都能给你一个可以直接照着做的完整方案。1. 先搞清楚Selenium和EdgeDriver到底是什么关系1.1 从一次“驱动崩了”的经历说起先讲个真实经历。有次我在内网环境搭自动化测试环境Chrome浏览器装不了只有Edge可以用。当时想得很简单把ChromeDriver换成msedgedriver.exe不就行了结果一启动就报错提示什么“This version of MSEdgeDriver only supports Edge version XX”。一看吓一跳我装了最新的EdgeDriver但电脑上Edge浏览器的版本早就是另一个了。查了半天才搞明白问题根源。这个经历其实反映了一个核心点很多人只知道“自动化测试需要装驱动”但不清楚驱动、浏览器、Selenium这三者之间到底是怎么协作的。一旦出现版本不兼容或者环境变量配置的问题就会一脸懵。1.2 WebDriver协议决定了整个架构Selenium本身不是一个“浏览器操作软件”它是一套标准。真正干活的时候Selenium的客户端库比如Python的selenium包、Java的selenium-java会通过HTTP协议把操作指令发送给一个“中间人”。这个中间人就是浏览器驱动比如EdgeDriver。驱动收到指令后再把指令翻译成浏览器底层的原生操作。也就是说驱动本质上是一个运行在本地的小型HTTP服务器它在Selenium脚本和浏览器之间做翻译。这里有个很关键的概念叫WebDriver协议。Selenium 3之前用的是JSON Wire ProtocolSelenium 4之后全面转向了W3C WebDriver标准协议。协议升级带来的最大变化是现在各个浏览器厂商自己的驱动都在逐步统一标准比如EdgeDriver和ChromeDriver的通信机制本质上是一样的因为它们都基于Chromium内核。1.3 Selenium 4之后的架构变化如果你用的是Selenium 4.x会注意到代码写法和Selenium 3有点不一样。Selenium 3的时候我们经常要自己启动一个驱动进程还要对浏览器做各种DesiredCapabilities配置。Selenium 4里引入了更简洁的Service类和Options类。拿Edge来举例你可以通过EdgeOptions设置浏览器参数通过EdgeService管理驱动进程。这样写的好处是代码更清晰驱动进程的启停也更可控不容易出现用完了驱动进程还挂在后台的情况。另外Selenium 4还自带了一个Selenium Manager理论上它能自动帮你匹配和下载驱动。但实际用下来在内网或者网络受限的环境里Selenium Manager经常拉不到驱动所以手动管理EdgeDriver还是我们必须掌握的技能。2. 环境准备EdgeDriver选型与下载2.1 怎么判断你自己该下载哪个版本的驱动这个问题是很多新手的第一个坎。去EdgeDriver下载页一看密密麻麻全是版本号根本不知道选哪个。其实判断逻辑很简单就一句话驱动的主版本号必须和浏览器的版本号保持一致。具体怎么看你本机Edge的版本打开Edge浏览器在地址栏输入edge://settings/help回车之后就能看到类似“版本 127.0.2651.72 (正式版本) (64位)”这样的信息。这里的127就是主版本号。然后去EdgeDriver下载页找到对应127.x.x.x的版本下载就行。驱动的主版本和浏览器主版本保持一致一般就能跑起来不用纠结后面的小版本差异。我自己有个习惯每隔一段时间就会主动检查一下浏览器版本因为Edge默认是自动更新的。你周一装好的驱动可能周三浏览器一更新就失效了。这也是自动化环境里“昨天还好好的今天突然跑不了”的头号原因。2.2 下载地址和安装配置实操EdgeDriver官方下载地址是Microsoft Edge WebDriver的开发者页面。页面上会提供几个渠道Windows稳定版、macOS版、Linux版甚至还有ARM架构的版本。下载下来是一个压缩包解压之后得到msedgedriver.exe。怎么放这个文件有三种常见做法第一种把驱动文件放到脚本所在目录或者项目里的driver目录然后在代码里显式指定路径。这种方式最推荐因为一台机器上可能同时存在多个EdgeDriver版本按项目隔离就不会互相干扰。第二种把驱动所在目录加入系统的PATH环境变量。这样代码里不写路径也能直接加载但有了Selenium Manager之后这种做法的必要性在降低。第三种交给Selenium Manager自动管理。前提是你的环境能正常访问微软的下载地址网络不通就得老老实实手动下载。我第一次配环境的时候图省事把驱动直接丢到了系统PATH里结果后来不同项目需要不同版本的驱动改来改去特别痛苦。现在我做项目都会在项目目录下建一个drivers文件夹每个项目用各自的驱动版本再配合代码里的相对路径干净又省心。2.3 常见踩坑Edge Legacy和Edge Chromium的区别很多人不知道Microsoft Edge其实有两个完全不同的版本。旧版叫Edge Legacy用的是自家EdgeHTML内核Windows 10早期版本自带的就是它新版叫Edge Chromium也就是现在大家用的这个基于Chromium内核和Chrome同源。这带来了一个很实际的问题Edge Legacy用的驱动和Edge Chromium用的驱动完全不一样。如果拿新版驱动去驱动旧版浏览器或者反过来都会失败。判断方法是看浏览器具体版本号如果版本号在80以下而且界面风格偏旧很可能就是Legacy版不过现在极少数人才会碰到旧版了。这个坑在2020年那会儿特别常见现在基本没有人提了但偶尔有同事换了旧电脑来问问题我还是会先确认一下他装的是哪个浏览器内核。别小看这一步很多人配置了半天驱动就是起不来最后发现是浏览器版本和驱动版本八字不合。3. 第一个自动化脚本从启动浏览器到完成一次搜索3.1 初始化驱动两种方式的对比写代码之前先确认你的Python环境里已经装了Selenium库。装好之后初始化Edge驱动有两种写法。先看最基础的写法from selenium import webdriver # 不带参数直接创建前提是msedgedriver.exe已经加入PATH driver webdriver.Edge()这种写法很简单但如果驱动不在PATH里就会报错。我建议用显式指定Service的方式可读性好也不依赖环境变量from selenium import webdriver from selenium.webdriver.edge.service import Service # 指定驱动路径 service Service(rD:\project\drivers\msedgedriver.exe) driver webdriver.Edge(serviceservice) driver.get(https://www.bing.com) print(driver.title) driver.quit()注意driver.quit()这行很多人写脚本会漏掉。漏掉之后Edge窗口会一直开着驱动进程也一直占着后台时间久了整个环境里全是残留进程再来跑脚本时各种诡异报错。我自己一般在每个用例结束之后用quit()而不用close()因为close()只是关掉当前标签页驱动进程还在quit()才是彻底释放所有资源。3.2 基本浏览器操作与常用配置除了打开网页浏览器级别的操作也经常用到。比如窗口最大化、设置窗口大小、刷新网页、向前向后翻页这些都是做自动化测试时的基础操作。driver.maximize_window() # 窗口最大化 driver.set_window_size(1280, 800) # 自定义窗口大小 driver.back() # 浏览器后退 driver.forward() # 浏览器前进 driver.refresh() # 刷新页面 driver.delete_all_cookies() # 清除全部CookieEdge和Chrome一样也支持很多启动参数。比如要做无人值守的任务可以开启无头模式也就是不弹出浏览器窗口直接后台运行from selenium.webdriver.edge.options import Options options Options() options.add_argument(--headlessnew) # 新版无头模式 service Service(rD:\project\drivers\msedgedriver.exe) driver webdriver.Edge(serviceservice, optionsoptions)为什么这里要特意提一下--headlessnew因为旧版的无头模式--headless在Chromium内核升级之后有些页面渲染行为会不一样可能出现截图一片黑、元素定位不到之类的问题。新版无头模式更贴近真实浏览器行为遇到某些反爬检测或者复杂的渲染逻辑时会稳定很多。3.3 等待机制是脚本稳不稳的关键新手很容易忽略等待写完脚本就急着跑结果页面元素还没加载出来定位就报错了。最常见的错误是NoSuchElementException而根源往往是“元素还没出现脚本就去点它了”。解决这个问题有两个通用方案。第一个是隐式等待一行代码全局生效driver.implicitly_wait(10)意思是每次找元素时如果没找到最多等10秒。这个设置对页面上所有元素生效简单粗暴但缺点是没办法处理某些特殊场景——比如元素一开始存在但不可点击或者要等某个元素消失。第二个是显式等待更精细。比如等“购物车”按钮变成可点击状态再操作from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 10秒内每隔500毫秒检查一次直到元素可点击 button WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, cart-button)) ) button.click()显式等待的expected_conditions有很多种除了element_to_be_clickable还有presence_of_element_located元素已在DOM中、visibility_of_element_located元素可见等。用得最多的是这几种覆盖了90%以上动态页面的场景。我个人的经验是隐式等待显式等待配合使用。隐式等待设置一个兜底时间比如10秒处理大部分加载场景遇到需要“等一段时间才能操作”的关键节点时再单独用显式等待。千万别把隐式等待设得太长比如30秒因为如果页面元素真的不存在每次定位都傻等30秒脚本跑起来会慢到怀疑人生。4. 元素定位实战从基础语法到业务场景4.1 八种定位方式怎么选Selenium里元素定位有八种方式id、name、class name、tag name、link text、partial link text、xpath、css selector。其中前六种用法比较固定遇到就优先用尤其是id高效又稳定。麻烦的是xpath和css selector它们能处理复杂场景但新手容易陷入选择困难。我给你一个选择优先级这是很多资深测试的经验总结定位方式适用场景稳定性id元素有唯一的id属性极高name表单类元素有name属性高link_text / partial_link_text超链接文本完全匹配/部分匹配高css selector元素有稳定的class或属性组合较高xpath属性不清晰需要根据DOM结构或文本定位中高举个例子登录页的输入框一般都有id或name直接用By.ID最省事但如果是页面上的菜单项很可能要依赖xpath按文本定位。4.2 根据菜单名定位元素的几种方案“根据菜单名定位元素”是很多网页自动化场景里的常见需求。页面上有个下拉菜单菜单项是动态生成的没有固定的id怎么定位这里有一个很实用的xpath技巧。假设菜单文本是“用户管理”那么可以用driver.find_element(By.XPATH, //*[text()用户管理])这段XPath的意思是在页面任意位置找任意标签只要其文本内容正好等于“用户管理”就选中它。如果菜单项本身是链接也可以在a标签范围内找driver.find_element(By.XPATH, //a[text()用户管理])如果菜单名很长或者包含动态部分可以用contains做模糊匹配driver.find_element(By.XPATH, //a[contains(text(),用户管理)])这段代码的意思是找所有a标签只要文本里包含“用户管理”就算数。不过这里要提醒一句contains(text(), ...)在匹配嵌套结构的时候会有坑。比如divspan用户/span管理/div这种span里的“用户”和div里的“管理”是两个文本节点直接text()匹配不到。遇到这种情况可以用.//来匹配所有子文本节点比如//div[contains(.,用户管理)]。我当时第一次遇到菜单定位不到就在这上面卡了很久后来才知道contains(., ...)和contains(text(), ...)差挺多的。4.3 购物车页面自动化的完整案例“自动化测试购物车页面”是电商测试里的高频场景搜索热词里也提到了。我这里给一个完整的流程示例从搜索商品到购物车结算的操作链路。假设我们要自动完成打开京东首页 - 搜索“手机” - 点击第一个商品 - 加入购物车 - 进入购物车页面 - 修改数量 - 去结算。from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC service Service(rD:\project\drivers\msedgedriver.exe) driver webdriver.Edge(serviceservice) driver.maximize_window() try: # 打开首页 driver.get(https://www.example.com) # 搜索商品 search_input WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.ID, search-input)) ) search_input.send_keys(手机) search_input.submit() # 等待搜索结果中的商品卡片出现 product_card WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, //div[classproduct-list]//div[contains(class,product-item)][1])) ) product_card.click() # 切到商品详情页的窗口 window_handles driver.window_handles driver.switch_to.window(window_handles[-1]) # 点击加入购物车 add_cart_btn WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, //button[contains(text(),加入购物车)])) ) add_cart_btn.click() # 进入购物车页面 cart_btn WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, //span[contains(text(),购物车)])) ) cart_btn.click() # 修改商品数量为2 quantity_input driver.find_element(By.XPATH, //input[aria-label商品数量]) quantity_input.clear() quantity_input.send_keys(2) # 点击去结算 checkout_btn WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, //button[contains(text(),去结算)])) ) checkout_btn.click() print(购物车流程执行成功) finally: driver.quit()这个例子看起来简单但实际操作中有几个细节值得单独拿出来说。第一switch_to.window(window_handles[-1])。点击商品卡片经常会新开一个标签页如果不切换窗口后面的定位全都会失效。driver.window_handles返回所有窗口的句柄列表最后一个是最新打开的窗口所以直接用索引-1。第二购物车里的数量输入框有的网站是用clear()再send_keys()有的网站是只读的需要先点击后手动选择数量。遇到只读输入框不能硬改可以先找加减按钮或者用JavaScript改值这个要看具体页面实现。第三“去结算”按钮在商品数量变化后可能有一个短暂的重算过程如果不加等待直接点击可能会出现“按钮不可点击”的报错。所以关键操作前最好都先加一个显式等待。4.4 定位不到元素时的三招排查套路做定位时最容易遇到的就是“定位不到元素”我这里分享三个排查步骤基本能解决90%的问题。第一步先看元素是不是在iframe里。如果页面用了iframe直接find_element肯定找不到必须先从主文档切到iframe内部操作完再切回来driver.switch_to.frame(frame-id) # 按id或name切换 driver.switch_to.frame(0) # 按索引切换 driver.switch_to.default_content() # 切回主文档第二步看是不是元素之前还有一层弹窗或者遮罩。常见的场景是登录弹窗盖住了目标按钮元素虽然能定位到但点击的时候被拦截了。这种可以用element.click()看报错信息也可以先关闭弹窗再操作。第三步用XPath的contains或者CSS的模糊匹配寻找替代方案。比如一个按钮的class是btn btn-primary w-100完全匹配btn不行但contains(class, btn)就能找到。这个方法特别适合动态类名场景。我踩过最惨的一次是一个菜单项在鼠标悬停之后才展开子菜单。我一开始直接定位子菜单里的“导出”按钮怎么都找不到后来改成先悬停在父菜单上等子菜单出现再定位子元素问题就解决了。这种“先hover再操作”的场景在菜单测试里太常见了。5. 常见问题与排查技巧实录5.1 高频报错对照表用Selenium EdgeDriver做自动化有一批报错属于“高频经典款”我整理成一张速查表方便大家直接对照。报错信息可能原因解决办法SessionNotCreatedException: This version of MSEdgeDriver only supports Edge version XX驱动版本与浏览器版本不匹配按浏览器主版本号重新下载对应驱动WebDriverException: unknown error: cannot find Chrome binary系统找不到浏览器可执行文件检查Edge安装路径是否正常或用Options指定binary_locationNoSuchElementException元素未加载或定位方式不对先确认DOM是否存在再检查iframe、遮罩层TimeoutException等待条件在指定时间内未满足检查等待表达式是否写错看页面是否真的加载了目标元素ElementNotInteractableException元素存在但不可交互可能是被遮罩层盖住或者元素需要先滚动到可见区域selenium.common.exceptions.InvalidArgumentException初始化参数传错检查Service/Options的写法确认Selenium版本是4.x表格里每一项都是实战出来的经验尤其第一项基本是每个从“浏览器自动更新”引起的噩梦中都遇到过。我建议大家在写脚本前先做一次“浏览器版本 - 驱动版本”一致性检查两分钟的事能省下后面半小时的排查时间。5.2 “浏览器自动更新导致驱动失效”的三种应对策略如果你所在团队有长期维护的自动化项目肯定会被“浏览器自动更新”恶心过。昨天脚本还好好的今天一跑就报版本不匹配。针对这个问题有几种应对策略。第一种直接在Edge的启动参数里禁用自动更新。Windows上通过组策略或者计划任务来关闭Edge的自动更新服务。这个方案适合公司环境中统一分发浏览器的场景所有机器都不更新驱动版本一直固定。但这需要一定的权限个人电脑上不一定好用。第二种在脚本启动前动态检查版本并自动驱动匹配。Python里可以通过读取driver.capabilities[browserVersion]拿到当前Edge的实际版本再据此去下载对应的驱动。这套自动化检查逻辑比较高级但工程化价值高适合测试框架做到一定程度的人。第三种最省心利用Selenium Manager。版本够新的Selenium库会在没有手动指定驱动时自动下载匹配的驱动。前提是网络能访问外网的下载地址。内网环境就老老实实手动管理驱动版本吧。我个人建议如果没有特殊权限限制干脆在项目中固定一个Edge驱动版本目录浏览器不更新就不动驱动浏览器一旦更新立刻同步换驱动。同时把驱动文件按版本号命名存成历史版本需要回退时直接切路径。5.3 一个容易被忽视的坑多个EdgeDriver进程残留还有一个特别容易被忽视的问题是驱动进程残留。脚本出错后如果用的是driver.close()而不是driver.quit()或者脚本中途崩溃msedgedriver.exe这个进程可能还留在系统后台。残留进程多了之后再跑新的脚本有时会报“端口被占用”之类的错误或者新脚本初始化特别慢。因为Selenium和驱动之间的通信默认用的是一组固定的端口一旦被占满新会话就建不起来。排查办法很简单打开任务管理器找msedgedriver.exe的进程全部结束后再重新跑脚本。如果频繁出现残留进程就要反思代码里有没有异常处理不完善的地方别忘了在finally块里调用quit()。5.4 帮助文档和社区资料怎么看遇到Selenium和EdgeDriver的问题很多人第一反应是去搜索引擎复制报错信息这没错但有些一手资料比搜索引擎更值得看。Selenium官方文档是一个不错的起点现在官方文档对Selenium 4的说明很详细。EdgeDriver的对接说明、下载地址和版本比对在Microsoft Edge WebDriver官方页面有完整说明记得那里的更新时间很及时每次Edge大版本更新后驱动也会同步更新。看这些资料的时候有个小技巧先确认自己的Selenium版本、浏览器版本、驱动版本再去查对应文档。因为网上的教程质量参差不齐很多是基于Selenium 3的老写法照抄到Selenium 4上可能会碰壁。版本信息对齐了再去查才能少走弯路。5.5 几个提升稳定性的工程化建议做自动化测试久了你会发现脚本能不能稳定跑技术细节只占一半工程习惯占另一半。我最后分享几个比较通用的工程化建议。第一所有页面元素对象尽量抽离出来不要散落在脚本里。比如定义一个CartPage类把购物车页面的所有元素和操作封装成方法这样页面前端改了你只需要改一个地方。第二截图和日志是必不可少的。脚本失败时自动截图保存现场日志里记录当前操作的步骤和页面URL排查问题会快很多。Selenium里截图很简单driver.save_screenshot(failure_20250108_1530.png)第三分层管理配置。驱动路径、基础URL、等待时间这些都放在配置文件里不要写死在代码里。环境切换的时候只改配置不动代码。这个建议听起来普普通通但真正做到的项目其实不多。这些建议不是Selenium或者EdgeDriver特有的但当你真的把自动化测试发展到上百个用例时这些工程细节会直接影响维护成本。最后再说几句做了这么多年自动化测试最大的一个体会就是工具本身不复杂复杂的是环境。Selenium这么多年还是自动化领域的主力就是因为它的生态足够完善配合EdgeDriver用起来又稳又顺手。如果你刚开始接触这套东西别急着写复杂脚本先把浏览器版本、驱动版本、第一个启动脚本跑通再一点点往里面加东西。环境问题解决了剩下的就是元素定位和业务逻辑难度会下降很多。真要是卡在哪个报错上按照我上面写的对照表一条条排查基本能覆盖一大半的项目场景。搞自动化本身是个熟练活多踩几次坑多记几次笔记后面就会越来越顺。