首页/新闻资讯/正文详情

Selenium自动化测试核心函数实战:从元素定位到等待机制

发布时间:2026/9/21 1:33:39 来源:云帆数科 栏目:资讯中心
Selenium自动化测试核心函数实战:从元素定位到等待机制
我一直觉得自动化测试这行里Selenium 是那种“绕不开的老伙计”。不管你是刚接触爬虫想抓点动态数据还是团队要做 Web 端回归测试只要你需要让浏览器自动干点活最后基本都会撞到它。这工具出道十几年生态成熟资料也多但正因为资料太多太杂新手反而容易懵——尤其是“函数部分”这东西官方文档写得又散又硬很多人学完 find_element 就卡住了后面的显式等待、下拉框处理、鼠标键盘操作要么不知道有现成的函数要么不知道什么时候该用哪个。这篇文章我想换个讲法不按官方文档的目录念经而是从一个实际项目的角度走一遍从环境准备、驱动配置、元素定位再到等待机制、关键函数封装、常见报错排查把你写自动化脚本时真正会用到的函数串起来讲。Selenium 四个单词拆开看就是 Selenium 的自动化测试框架这名字挺有意思其实就点明了它的核心价值——让浏览器按脚本办事。我尽量把每个函数放到场景里讲告诉你它解决什么问题、背后机制是什么、踩过哪些坑而不是光给个语法。如果你正准备入门或者用了一阵子但总觉得卡手这篇文章应该对你有用。1. Selenium 项目设计与思路拆解1.1 为什么是 Selenium它到底解决了什么问题先聊个基础问题为什么做 Web 自动化时大家优先选 Selenium而不是别的方案简单说Selenium 解决了“模拟真实用户操作”这件事。它通过 WebDriver 协议和浏览器进行通信由浏览器原生驱动如 chromedriver、geckodriver执行指令所以它启动的是一个真实的浏览器实例页面上的 JavaScript、AJAX 请求、Canvas 渲染都按照浏览器的真实逻辑运行。这一点是很多轻量级方案比不了的——requests 这样的 HTTP 库拿到的只是服务器返回的原始 HTML页面里由脚本动态渲染出来的内容它根本看不到而 Playwright、Puppeteer 这类后来者虽然在某些场景性能更好、API 更现代但 Selenium 的优势在于“历史悠久、资料多、学的人多、团队协作时别人接手成本低”而且它支持的语言非常广Java、Python、C#、JavaScript 都有官方绑定我在实际项目里见过最老的 Java 版本和最新的 Python 版本混用都能兼容。还有一个我在多个项目里实际比较下来的点Selenium 适合“从零到一快速搭建”。比如公司里一个内部管理系统要发版前做冒烟测试你不想手点 40 个页面用 Selenium 写 100 行脚本就能把主流程跑一遍出个截图和日志这活就算接住了。它不追求极致的执行速度但胜在稳定、通用、能落地。1.2 自动化测试框架的思路功能分层与函数封装说到“包括函数部分”我理解你想知道的不只是“Selenium 有哪些 API 函数”更关键的是“怎么用函数把代码组织得能维护”。我早期写自动化脚本的时候犯过一个典型错误把所有逻辑堆在一个 Python 文件里五六个用例全写在一个 main 里定位表达式、等待逻辑、数据校验全揉在一起。结果用例一多改一次页面结构要改五六个地方维护成本特别高。后来我参照了 Page Object 模式把项目拆成了清晰的三层结构第一层是“页面对象层”每个页面写一个类类里封装好这个页面的元素定位信息和对应操作函数。比如一个登录页面类它只负责暴露输入用户名、输入密码、点击登录、获取错误提示这几个方法。第二层是“用例层”负责组织具体的操作序列和数据校验它只和页面对象层打交道不直接写 find_element。第三层是“数据与工具层”存放配置、公共等待函数、截图工具等。这样做的好处很直观页面结构变了只改页面对象层业务逻辑变了只改用例层公共操作改了只改工具层。函数封装在这个架构里不是可有可无的附赠品而是整个工程能不能持续跑下去的关键。你去看那些能运行几百条用例的自动化测试项目背后一定是严格分了层的。不过话说回来如果你是第一次接触 Selenium也别一上来就搞这么重的架构。先把单个函数跑通再谈工程化。我建议的学习路径是手动写一个几十行的登录脚本再用函数重构它最后再重构到分层的结构。一步一个台阶心里就有底了。2. 环境准备与基础函数入门2.1 安装 Selenium这步没你想象的那么简单安装本身不复杂pip install selenium但这里有两个容易踩的坑我一个个说。第一你可能会遇到“pip 无法将‘pip’项识别为 cmdlet、函数、脚本文件或可运行程序的名称”这种报错。这是 Windows PowerShell 的典型提示意思是当前终端里找不到 pip 命令。最稳妥的解法是去 Python 官网装 Python 时勾选 “Add python.exe to PATH”然后重开终端。如果你已经装好了 Python 但没加 PATH也可以改用这个命令python -m pip install selenium用python -m pip显式通过 Python 解释器调用 pip 模块绕开 PATH 问题。同理如果你遇到“claude”、“git”、“npm”之类命令找不到的错误思路都是一样的先看环境变量别慌着重装。第二安装不是终点你还需要一个浏览器驱动。比如 Chrome 用 chromedriverFirefox 用 geckodriver。WebDriver 就像浏览器和 Selenium 之间的翻译官Selenium 发出的命令要靠它翻译成浏览器能懂的指令。驱动版本必须和浏览器版本匹配比如你 Chrome 是 126.x就下载对应 126.x 的 chromedriver。下载地址是 Chrome for Testing / chromedriver 的官方站点解压后可以放到 Python 安装目录的 Scripts 文件夹里只要这个目录在 PATH 里也可以在代码里用Service指定路径。我实际测试下来驱动路径问题是最容易卡住新手的。建议最开始的练习阶段代码里显式写驱动路径因为这样最省心。from selenium import webdriver from selenium.webdriver.chrome.service import Service2.2 第一个函数 get()打开页面其实很简单装好后打开浏览器加载一个页面核心就两个函数from selenium import webdriver driver webdriver.Chrome() driver.get(https://example.com) print(driver.title) driver.quit()这里的webdriver.Chrome()负责启动浏览器并建立会话返回值就是我们操作浏览器的主入口对象习惯上叫 driver。driver.get()负责让浏览器导航到指定 URL等页面完全加载完再返回严格说是document.readyState变成 complete但页面上 JS 后续发起的异步请求不一定结束。driver.title是一个属性不是函数返回当前页面的标题。最后一定要调用quit()它可以关闭浏览器并清理驱动进程。我见过不少新手习惯用driver.close()但 close 只关闭当前标签页标签页不会真正杀掉驱动进程。如果脚本执行报错了Chromedriver 进程就可能在后台残留。所以日常脚本里收尾一律用quit()或者配合 try/finally 确保它一定执行。下面这个是更安全的写法driver webdriver.Chrome() try: driver.get(https://example.com) finally: driver.quit()如果你的脚本用到 pytest 这类测试框架还可以用 fixture 来做 setup 和 teardown那管理起来更优雅。3. 元素定位函数自动化脚本的核心基本功3.1 find_element 系列函数八种定位策略拿到了 driver你接下来要面对的就是真正的硬骨头——找到页面上的元素并操作它。Selenium 的 WebDriver 提供了八种定位策略在 Python 的 Selenium 库里对应By枚举ID、NAME、CLASS_NAME、TAG_NAME、LINK_TEXT、PARTIAL_LINK_TEXT、CSS_SELECTOR、XPATH。from selenium.webdriver.common.by import By driver.find_element(By.ID, login-username) driver.find_element(By.NAME, password) driver.find_element(By.CLASS_NAME, btn-login) driver.find_element(By.TAG_NAME, button) driver.find_element(By.CSS_SELECTOR, form input[nameuser]) driver.find_element(By.XPATH, //div[classlogin-box]//button)注意Selenium 4 的推荐写法是find_element(By.ID, xxx)旧版的find_element_by_id(xxx)已经被移除了。如果你在网上看教程时发现代码跑不通很多都是因为版本差异。这么多定位策略怎么选我的经验是有一个优先级能用 ID 就优先用 ID它最短也最稳定。没有 ID 就找有没有 name再不行用 CSS 选择器。XPath 是兜底方案它能处理非常复杂的页面结构但也要承受性能开销而且写得不规范时非常脆弱。find_element还有一个容易被忽视的细节它默认只会返回第一个匹配的元素。如果页面上有多个“确定”按钮你用find_element(By.XPATH, //button[text()确定])拿到的可能不是你想要的那一个。这时候要么用更精确的 XPath 带上祖先节点的信息要么直接上find_elements注意是复数它返回一个列表你可以按索引取也可以遍历排查。3.2 和 CSS_SELECTOR 与 XPath 相关的实用函数CSS 选择器在定位动态页面时非常好用但新手往往只会写标签选择器和类选择器。实际做项目你会发现几个高级语法效率很高属性选择器可以精确定位有特定属性的元素子元素选择器能表示直接父子关系nth-child()能按序号选元素。比如要定位一个列表里的第二项你用ul li:nth-child(2)一行就搞定了换成 XPath 写起来反而绕。XPath 里我常用的几个函数是text()文本判断、contains()模糊匹配、starts-with()前缀匹配、following-sibling::兄弟节点、ancestor::祖先节点。# 按钮文字包含“登录”二字的按钮 driver.find_element(By.XPATH, //button[contains(text(), 登录)]) # 找到“用户名”输入框所在的表单区域后再向下找提交按钮 submit_btn driver.find_element( By.XPATH, //form[idform1]//button[typesubmit] )这里有件事我必须提醒你XPath 里如果页面结构经常变动你的定位方式就是定时炸弹。比如div[1]/div[2]/div[3]这种索引式定位只要 UI 调整一下就全废了。更稳的做法是优先选稳定的属性id、name、data-xxx再用相对层级约束范围。页面元素加了 outlook 之类的属性也可以利用上。3.3 与元素交互的核心函数click、send_keys、clear定位到元素后常见交互就是三个函数click()、send_keys()、clear()。click()模拟鼠标左键点击。send_keys()向输入框输入文本。这里有个隐藏细节即使元素没有获得焦点Selenium 也会先自动聚焦再输入大多数情况下没问题但碰上特殊情况比如输入框有自定义的 JS 事件自动聚焦可能触发不了。clear()用于清空输入框已有的内容。你可能会想清空不就是选中文字再删掉但很多场景下clear()更可靠尤其对 date 类型、select 类型的输入框。另外提一个大家容易忽略的send_keys 不仅能输入字符串还能发送特殊按键比如 Enter、Tab、CtrlA。这些按键在selenium.webdriver.common.keys.Keys里定义好了比如按 Enter 提交搜索from selenium.webdriver.common.keys import Keys search_box driver.find_element(By.NAME, q) search_box.send_keys(selenium) search_box.send_keys(Keys.ENTER)在很多搜索场景里这个Keys.ENTER比找“搜索”按钮再点击要省事得多还减少了元素定位失败的概率。4. 等待机制函数自动化稳定性的大半条命4.1 为什么要等待关于加载时间的真相写自动化脚本时最令人抓狂的报错就是selenium.common.exceptions.NoSuchElementException: Message: no such element明明浏览器页面上能看到这个元素脚本却找不到。这大概率是“时机”问题——页面还没有完全渲染完脚本就尝试去查找了。浏览器加载一个现代网页的过程大致是拿到 HTML 文档 - 解析并生成 DOM 树 - 加载 CSS、JS - 执行 JS - 可能还有异步接口请求 - 拿到数据后再次渲染。Selenium 的get()只在document.readyState变为 complete 时返回但这只代表初版加载完成页面上 JS 动态渲染的那部分内容可能还在路上。所以等待不是“可选的优化”而是“必要的保障”。Selenium 提供的等待机制本质上就是帮你不断询问“好了吗”好了就继续执行超时了才报错。4.2 隐式等待全局兜底方案driver.implicitly_wait(10)隐式等待给 driver 设置一个全局时间上限。之后每次调用find_element或find_elements如果元素没有立即出现WebDriver 会在设定的时间内轮询去查找超时才抛NoSuchElementException。它的优点是简单一次设置全局生效。缺点也很明显它对find_element有效但对click和send_keys时元素不可用、被遮挡这类状态变化没有帮助而且如果设置时间太长某些“本应找不到元素”的场景也会白白等待拖慢整个脚本。我建议隐式等待作为兜底可以设但不要设太长比如 5 秒就够核心场景再用显式等待精确控制。4.3 显式等待与 expected_conditions 函数族显式等待的核心是WebDriverWait配合until()方法以及一个巨大的条件函数库expected_conditions通常简写成 EC。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC wait WebDriverWait(driver, 10) search_btn wait.until( EC.element_to_be_clickable((By.CSS_SELECTOR, .search-btn)) ) search_btn.click()这段代码的意思最多等 10 秒每 500 毫秒默认轮询间隔检查一次直到“搜索按钮可点击”这个条件满足为止然后返回这个元素。EC 里条件函数非常多我按使用频率列一个表条件函数用途常见场景visibility_of_element_located等待元素出现在 DOM 中且可见页面加载完成后处理核心元素element_to_be_clickable等待元素可见且可点击不 disabled点击前等待按钮激活presence_of_element_located等待元素出现在 DOM 中不要求可见判断元素是否已插入页面text_to_be_present_in_element等待元素包含指定文本等待提示信息弹出element_located_to_be_selected等待下拉框选项被选中处理 select 回显alert_is_present等待弹窗出现处理 alert 弹窗这里有一个很重要的机制需要理解presence和visibility的区别。DOM 里存在不等于用户能看到被隐藏了display:none、被遮挡都属于不可见。点击操作要求它可见且可点击所以点击前用element_to_be_clickable才是对的presence_of_element_located这个方法相对而言更原始容易造成“存在但点不了”的尴尬。另外自定义等待条件也很方便你完全可以把until()当成一个通用的轮询工具传任意返回真值的函数wait WebDriverWait(driver, 10) wait.until(lambda d: d.execute_script(return document.readyState) complete)这种用法的好处是不需要显式定义一个 EC 条件直接传入一个匿名函数代码更内聚。4.4 强制等待与 time.sleep为什么我劝你慎用市面上很多教学都写了time.sleep(3)然后快速插入几个硬等待看代码好像稳定了。但我实际维护过的项目里硬等待就是一把双刃剑。假设你 sleep 3 秒页面上接口 5 秒才返回你就白等那 3 秒反过来如果你 sleep 2 秒页面接口 5 秒才回来你依然会报错。网络环境一波动你只能把 sleep 值调大然后整个测试套件的耗时成倍上涨。我见过一个项目300 条用例里面有 50 处time.sleep(5)跑一轮就得额外花 4 分钟——纯属浪费。所以我给的方案是把显式等待封装成公共函数作为默认方案尽量不用强制等待。只有少数场景比如页面正在播放动画时强制等待可能偶尔需要但那是例外不是常态。5. 核心操作函数下拉框、弹窗、截图与高级交互5.1 Select 函数处理下拉选择框的标准姿势网页里的下拉框主要有两类原生select标签和用 div/ul/li 模拟的自定义下拉。这两类操作方式完全不同。对于原生selectSelenium 专门提供了Select类它能帮你处理选择逻辑from selenium.webdriver.support.ui import Select select_el driver.find_element(By.ID, city) select Select(select_el) select.select_by_index(1) # 按索引选 select.select_by_value(guangzhou) # 按 value 属性选 select.select_by_visible_text(广州) # 按下拉显示的文字选日常工作中select_by_visible_text最常用因为对用户来说选择的其实是看到的那行字而且就算 value 变了只要 UI 文字没变脚本依然能跑。不过要注意select_by_visible_text是精确匹配而不是模糊匹配你在“广州市”和“广州市天河区”同时存在的下拉框里传“广州”是选不中的需要传完整的可见文本。顺便说一句Select 还提供了deselect方法仅当下拉框设置了multiple属性时才有效普通单选下拉框调deselect_all()会抛异常。我碰到过好几个同学在这里踩坑看到报错就以为脚本坏了其实是因为根本没有多选能力。对于自定义下拉框没有现成的 Select 可以用思路是点击下拉区域再在候选项里找到目标项。比如这种模拟下拉点击展开后可以用 XPath 文本精确寻找选项这样比较稳。5.2 ActionChains处理鼠标悬停、拖拽等复杂操作有些操作是“组合动作”比如悬停下拉菜单、右键、双击、拖拽。这些都属于高级交互Selenium 用ActionChains来管理。from selenium.webdriver.common.action_chains import ActionChains ac ActionChains(driver) menu driver.find_element(By.CSS_SELECTOR, .nav-item) ac.move_to_element(menu).perform() # 鼠标悬停 source driver.find_element(By.ID, drag-source) target driver.find_element(By.ID, drag-target) ac.drag_and_drop(source, target).perform() # 拖拽需要注意的关键点ActionChains支持链式写法你可以一次性串好几个动作最后.perform()触发。比如鼠标先移动到菜单再点击子项ac.move_to_element(menu).move_to_element(sub_menu).click().perform()这个链式调用顺序特别重要别指望它自动帮你排好中间路径。还有一个坑是有的项目页面需要真实鼠标事件用 ActionChains 可能就不够了需要用pyautogui这类系统级工具配合不过那是极端情况不多说。5.3 弹窗处理与 frame 切换网页弹窗有三种常见类型原生 alert/confirm/prompt、iframe 里的弹窗、自定义 div 弹窗。思路完全不同。原生 alert 在 Selenium 4 里有专门的接口from selenium.webdriver.common.alert import Alert alert Alert(driver) alert.accept() # 确定 alert.dismiss() # 取消 alert.send_keys(xxx) # 如果是 prompt可以输入如果你要判断弹窗是否出现不要直接调 Alert(driver) 而要先检查expected_conditions.alert_is_present()不然会等不到弹窗直接报NoAlertPresentException。frame 的处理则完全另一套逻辑。你遇到“元素明明存在但 find_element 失败”时有可能是这个元素嵌在 iframe 里。必须先切换进去再操作iframe driver.find_element(By.TAG_NAME, iframe) driver.switch_to.frame(iframe) # 在 iframe 里操作... driver.switch_to.default_content() # 切回主文档这个切换逻辑我印象很深之前我调一个系统嵌套了三层 iframe排查了很久才发现上级 iframe 的切换没成功。只要你用了 frame 相关函数就一定要记得用完切回默认文档否则后续元素定位会莫名其妙失败而且异常信息完全不提示原因。5.4 JavaScript 执行函数execute_script理论上 Selenium 能模拟用户操作但有些场景用户操作也做不到比如修改元素的属性、强制点击隐藏元素、拖动水平滚动条。这时候标准的 WebDriver API 反而不好处理可以用execute_script直接执行前端 JS# 滚动页面到底部 driver.execute_script(window.scrollTo(0, document.body.scrollHeight);) # 通过 JS 点击一个透明的、被覆盖的元素 btn driver.find_element(By.XPATH, //button[idhidden-btn]) driver.execute_script(arguments[0].click();, btn) # 修改元素的 placeholder 文本只对某些测试场景有意义 input_el driver.find_element(By.NAME, phone) driver.execute_script(arguments[0].setAttribute(placeholder, 请输入手机号), input_el)这里要记住一个语法细节execute_script里那些arguments[0]是占位符对应 JS 代码后面的 Python 参数。JS 中的arguments对象保存了传入的所有参数[0]就表示第一个参数。我常用 execute_script 做两类事情一是强制点击一些隐藏但可用的元素二是在等待数据加载时读取页面状态。它非常灵活但用多了以后也要注意脚本里 JS 报错时Selenium 可能不会很明确地反馈是哪一行出问题调试时要自己在浏览器控制台里先验证 JS 语法。5.5 截图、Cookie 管理与窗口控制最后说一下日常调试和项目交付时很有用的几个辅助函数。截图在排查问题时非常必要。脚本跑到一半失败了你光看报错日志往往看不出页面上到底是什么状态一张截图就全明白了。driver.save_screenshot(debug.png) driver.get_screenshot_as_file(/path/to/error.png) # 或者直接拿 PNG 二进制数据 png_data driver.get_screenshot_as_png()我在自己维护的项目里习惯在异常处理里统一把截图和当前 URL 一起保存下来然后写进日志。Cookie 管理主要是这几个函数get_cookies()获取当前所有 cookieadd_cookie()添加 cookiedelete_all_cookies()清理 cookie。最常见的用途是登录状态复用先用脚本登录一次拿到登录后的 cookie 写入文件下次运行时直接加载 cookie跳过登录流程能省下不少时间。窗口控制方面set_window_size()可以直接设定浏览器窗口尺寸这在做响应式页面验证时很常见driver.set_window_size(375, 812) # 模拟手机尺寸当然手机上通常是模拟设备不是简单改窗口尺寸但改窗口大小已经能粗略验证布局了。正式做移动端适配测试我建议还是用 DevTools Protocol 那套来模拟设备特性。6. 实操过程与核心环节实现一个完整的登录自动化脚本讲了一堆函数最终绕不过“跑一个真实脚本”这一步。我挑一个最常见的业务场景登录。这个场景覆盖了定位、输入、点击、显式等待、断言、截图这些核心环节麻雀虽小五脏俱全。下面是完整示例代码里我加了详细的解释import time 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 from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options def create_driver(): 初始化浏览器驱动带错误处理 options Options() # 取消自动化控制提示部分页面会检测这个标识这里只是降低干扰 options.add_experimental_option(excludeSwitches, [enable-automation]) svc Service(C:/path/to/chromedriver.exe) # 按你的实际路径改 driver webdriver.Chrome(servicesvc, optionsoptions) driver.implicitly_wait(5) return driver def login(driver, username, password): 核心登录流程函数 driver.get(https://example.com/login) # 用户名输入框 username_input WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.ID, username)) ) username_input.send_keys(username) # 密码输入框 password_input driver.find_element(By.NAME, password) password_input.send_keys(password) # 登录按钮 login_btn driver.find_element(By.CSS_SELECTOR, button[typesubmit]) login_btn.click() # 等待登录跳转后出现“用户中心”链接说明登录成功 WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.LINK_TEXT, 用户中心)) ) def is_login_success(driver): 登录成功的校验函数判断关键元素是否存在 try: WebDriverWait(driver, 3).until( EC.presence_of_element_located((By.LINK_TEXT, 用户中心)) ) return True except Exception: return False if __name__ __main__: driver create_driver() try: login(driver, test_user, 123456) if is_login_success(driver): print(登录成功) driver.save_screenshot(login_success.png) else: print(登录失败错误信息) err_el driver.find_element(By.CSS_SELECTOR, .error-tip) if err_el: print(err_el.text) driver.save_screenshot(login_fail.png) finally: driver.quit()看几个关键细节WebDriverWait(driver, 10).until(EC.visibility_of_element_located(...))这行会在执行前等待输入框真正显示出来。如果页面加载慢10 秒内会一直轮询超时后抛出异常。这里我用显式等待而没有用time.sleep原因前面已经讲过能稳就稳能快就快。is_login_success函数把“校验”抽出来单独做一个函数好处是登录失败时主流程不用中断可以继续去抓错误信息。实际项目里这种设计很常用尤其是你要跑多组账号做数据测试时失败不等于脚本问题可能是业务上的账号异常。driver.find_element(By.CSS_SELECTOR, .error-tip)如果页面不存在这个元素会抛异常所以我在前面先做一层判断拦截。这个写法健壮性更好推荐参考。如果多组用户需要重复执行可以在login()外层加循环或参数化。用 pytest 的话这就是一个典型的参数化用例模板。进一步想如果把定位信息提到类里把 username、password 作为入参传递那就已经很接近 Page Object 的雏形了。7. 常见问题与排查技巧实录7.1 高频报错速查表我整理了一份排查表这些问题基本都是新手和老手都会碰上的报错信息常见原因解决办法ModuleNotFoundError: No module named seleniumSelenium 未安装或解释器不对确认当前环境用python -m pip install seleniumWebDriverException: Message: chromedriver executable needs to be in PATH驱动不在 PATH 或未下载手动指定 driver 路径确认驱动与浏览器版本匹配SessionNotCreatedException: This version of ChromeDriver only supports Chrome version ...Chrome 浏览器和 chromedriver 版本不匹配下载与 Chrome 主版本一致的 chromedriverNoSuchElementException元素找不到可能是动态未加载完或定位表达式错误换显式等待先在浏览器 DevTools 验证定位表达式ElementNotInteractableException元素存在但不可见、被遮挡、被禁用检查元素是否在 iframe用element_to_be_clickable考虑 JS 点击StaleElementReferenceException元素被页面刷新或 DOM 改动后旧引用失效重新获取元素减少不必要的页面更新TimeoutExceptionWebDriverWait 超时后抛出检查定位表达式、等待条件、网络加载速度InvalidSelectorExceptionCSS / XPath 语法错误在浏览器控制台用$$()或$x()验证语法NoAlertPresentException尝试操作 alert 但没有弹窗用 EC.alert_is_present 预判检查弹窗是否被 js 延迟触发ElementClickInterceptedException: element click intercepted有元素遮挡目标点击区域等待遮挡元素消失滚到目标元素区域用 JS 点击7.2 “明明看到元素却定位失败”这类问题的定位思路这应该是问得最多的一类问题。我给你一个固定的排查路径照着走又快又稳第一步先确认程序里的 driver 操作的是不是你在浏览器里看到的那个页面。很多同学第一次就栽在这浏览器里开着调试页面脚本里正访问着另一个环境最后定位当然失败。还有一种是脚本启动了新的无痕窗口Cookie 不存在页面渲染逻辑完全不同。第二步用浏览器 DevTools 验证你的定位表达式。按 F12 打开控制台CSS 选择器用$$(your-selector)测试XPath 用$x(your-xpath)测试看结果是否符合预期。不要心存侥幸DevTools 说选不中那就是选不中。第三步检查 iframe。在 DevTools 的 Elements 面板里看看目标元素是不是嵌在 iframe 里了。如果是你就得先switch_to.frame再定位定位完成后切回默认。第四步如果是动态加载内容需要检查数据返回之后是不是还有 re-render。这种情况你刚定位的元素对象可能保留了旧状态换个新的尝试或者等待完成后再重新查找。第五步看隐藏元素。比如display:none或visibility:hidden的元素虽然可以定位到但不一定能交互。真的要操作它可以使用 JS 点击或者先把它置为可见再操作。这五步走完90% 的“找不到”问题都能解决。剩下的 10%我建议你把页面 HTML 快照和截图保存下来仔细对照确实是 Selenium 的 bug还是你的操作时序有问题。7.3 headless 模式与生产环境部署的注意事项很多人会在服务器上跑自动化任务这时浏览器不能弹出界面最常用的方案是 headless 模式options Options() options.add_argument(--headlessnew) options.add_argument(--window-size1920,1080) driver webdriver.Chrome(optionsoptions)常用参数补充几个--disable-gpu某些环境下避免 GPU 相关错误。--no-sandbox在 Docker 容器中可能需要否则会报 sandbox 权限错误。注意这个参数有安全含义最好只在可信环境中开启。--disable-dev-shm-usage容器里 /dev/shm 太小导致崩溃时加上。headless 模式和普通模式不是完全一样的。我实际遇到的差异有有些页面在 headless 下 UA 不同页面可能不展示某些元素有些视频或动画模块在 headless 下不被渲染还有极少数情况下本来能点击的元素在 headless 下报“不可见”。处理办法也不复杂可以在请求头里覆盖 User-Agent 伪装成正常浏览器或者改用--headlessnew这种新模式解决一部分兼容问题。7.4 从脚本到测试框架最后聊点工程建议。当你的脚本数量超过十个、而且经常要反复执行时就不应该继续用一堆if __name__ __main__去管理了。现在 Python 的自动化测试项目几乎都用 Pytest 做执行框架配合 Selenium 相当成熟。Pytest 的核心概念是 fixture。我给出一个最基础的示例import pytest from selenium import webdriver pytest.fixture def driver(): d webdriver.Chrome() yield d d.quit() def test_login_success(driver): driver.get(https://example.com/login) # 具体操作与断言 assert driver.title 登录fixture 里yield之前的代码在用例执行前运行之后的代码在用例执行后运行天然就是 setup/teardown。你可以在conftest.py里放公共 fixture这样所有测试用例都能共享同一个浏览器实例或同一个登录状态。用例多了以后断言失败时自动截图这类逻辑也可以放到 fixture 里统一处理。这一步做完后你的自动化脚本才算真正从“我会写函数”升级到“我能做项目”。实际工程里Pytest 的插件生态也弥补了 Selenium 很多不足比如失败重跑、allure 报告、并发执行都能靠插件搞定不需要自己从零搭建。8. 最后的几个经验分享写到这里前面已经把定位、等待、操作函数、常见问题都过了一遍。收尾前我再分享几条实在的经验都是在这条路上反复踩坑磨出来的。第一关于元素定位稳定是第一优先级。不要迷信最短的 XPath也不要贪图省事用绝对路径尽量找 id、name、data-* 属性这类稳定标识。实在没有再用contains(text(), ...)结合相对层级来约束。维护测试脚本最痛苦的就是前端改了页面结构脚本全崩而这个痛苦往往根源于定位方式太脆弱。第二等待策略不能一刀切。全局隐式等待兜底核心交互用显式等待强制睡眠尽量不写。具体等待时间要根据实际网络情况调不用追求“覆盖率 100% 稳”把一个轮询调长不如把 step 间逻辑梳理清楚。第三日志和截图是自动化的“眼睛”。排障效率在很大程度上取决于运行现场信息够不够。我的习惯是统一封装click、send_keys方法里面打点日志、缓存截图这样哪一步出问题日志能直接定位到具体代码行和页面状态不用现场漫无目的地瞎找。第四关注 Selenium 版本的持续演进。Selenium 4 引入的相对定位器相对定位器 By, 如locate_with之类的配合near、left_of和 WebDriver 生命周期管理机制很多项目里已经用得上。新版本并不总是兼容旧脚本升级前记得看官方的 changelog重点留意弃用函数。第五Selenium 不是万能的。如果你要做的自动化测试涉及小程序、桌面客户端或者是高并发压测Selenium 并不对口。它擅长的是 Web 端功能级自动化。工具选型比代码技巧更重要这个观点我觉得值得不断强调。这套东西从我开始学习 Selenium 到现在解决过不少麻烦也踩过不少坑。希望这篇文章能帮你少走几步弯路。用一位老前辈的话说自动化测试的重点不是“自动化”而是“测试”Selenium 只是个放大器你的思路强它就强。

相关推荐

开关电源调试避坑指南:20个经典错误与实战技巧
开关电源调试避坑指南:20个经典错误与实战技巧

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/21 1:33:39

Atlas 300V 24G深度解析:昇腾AI推理卡部署YOLO实战指南
Atlas 300V 24G深度解析:昇腾AI推理卡部署YOLO实战指南

1. Atlas 300V 24G到底是不是运算加速卡——一张卡的定位与真相先说结论:Atlas 300V 24G是运算加速卡,但它不是游戏卡,也不是通用计算卡,而是一张专门为AI推理场景打造的加速卡。很多人第一次看到“Atlas 300V”这个名字时&#x… · 2026/9/21 1:32:38

边缘AI SoC选型指南:12种异构计算组合与功耗权衡实战
边缘AI SoC选型指南:12种异构计算组合与功耗权衡实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/21 1:32:38

3步解决wordpress自己打包apk挂马危机与最佳实践
3步解决wordpress自己打包apk挂马危机与最佳实践

3步解决wordpress自己打包apk挂马危机与最佳实践 网站被黑挂马却不知从哪查起?别慌,这不仅是技术事故,更是法律风险。很多新手做wordpress自己打包apk时,为了省事直接调用第三方接口,结果APK里塞满恶意代码。本文拆解真实案例,给出可落地的最佳实践,帮你从根源堵住漏洞,守住网站底线。… · 2026/9/21 4:19:24

ARIS 工作流总览:从 idea 到 paper 的 13 条 pipeline 如何一次看全
ARIS 工作流总览:从 idea 到 paper 的 13 条 pipeline 如何一次看全

ARIS 工作流总览:从 idea 到 paper 的 13 条 pipeline 如何一次看全 【免费下载链接】Auto-claude-code-research-in-sleep ARIS ⚔️ (Auto-Research-In-Sleep) — Lightweight Markdown-only skills for autonomous ML research: cross-model review loops, idea … · 2026/9/21 4:06:05

南郊网站建设报价单背后的安全防线:3个实战案例揭秘
南郊网站建设报价单背后的安全防线:3个实战案例揭秘

南郊网站建设报价单背后的安全防线:3个实战案例揭秘 备案流程一头雾水?别急,南郊网站建设报价单里藏着比备案更深的坑。我见过太多老板盯着价格看,却忽略了“安全”二字。 上个月刚处理完一个 实战案例… · 2026/9/21 4:04:06

Roc 格式化器幂等性测试实战:从 issue 8851 快照看多行分发与字段访问的格式化处理
Roc 格式化器幂等性测试实战:从 issue 8851 快照看多行分发与字段访问的格式化处理

Roc 格式化器幂等性测试实战:从 issue 8851 快照看多行分发与字段访问的格式化处理 【免费下载链接】roc A fast, friendly, functional language. 项目地址: https://gitcode.com/GitHub_Trending/ro/roc 导读:本文以 Roc 编译器仓库中的快照测试… · 2026/9/21 4:04:05

TypePHP编译器API参考:程序化调用PHP AOT编译器的完整指南
TypePHP编译器API参考:程序化调用PHP AOT编译器的完整指南

TypePHP编译器API参考:程序化调用PHP AOT编译器的完整指南 【免费下载链接】typephp Compile PHP to Native Binaries 项目地址: https://gitcode.com/GitHub_Trending/ty/typephp TypePHP 是一款用 PHP 编写的原生 AOT 编译器(tpc)&a… · 2026/9/21 4:04:05

React Admin 实时数据提供者(Realtime Data Provider)接入完整指南:方法签名、内置适配器与自定义实现
React Admin 实时数据提供者(Realtime Data Provider)接入完整指南:方法签名、内置适配器与自定义实现

前端UI组件 【免费下载链接】react-admin A frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design 项目地址: https://gitcode.com/gh_mirrors/re/react-admin 点击查看 免费下载 本指南系… · 2026/9/21 4:04:05

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化
Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡… · 2026/9/21 0:02:39

Word表格编号全攻略:从列表编号到题注交叉引用
Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技… · 2026/9/21 0:02:39

从第一个站到第二个站:独立开发者的静态网站选型与落地实践
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&… · 2026/9/20 0:00:41

Claude Code 按智谱AI指南装完,ANTHROPIC_BASE_URL 改走 TaoToken 兼容通道行不行
Claude Code 按智谱AI指南装完,ANTHROPIC_BASE_URL 改走 TaoToken 兼容通道行不行

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/21 0:00:18

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程
agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程 【免费下载链接】agentic-awesome-skills AAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and … · 2026/9/21 0:00:18

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析
gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析 【免费下载链接】gin-vue-admin 🚀ViteVue3Gin拥有AI辅助的基础开发平台,企业级业务AI开发解决方案,内置mcp辅助服务,内置skills管理,… · 2026/9/21 0:00:18

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码