Selenium WebUI 自动化“避坑”指南——从常用 API 到 10 大高频问题

目录

[一、为什么 90% 的 UI 自动化脚本活不过 3 个月?](#一、为什么 90% 的 UI 自动化脚本活不过 3 个月?)

[二、Selenium必会 API 速查](#二、Selenium必会 API 速查)

三、实践

[四、10 大高频异常"症状 → 病因 → 处方"](#四、10 大高频异常“症状 → 病因 → 处方”)

五、可复用的工具函数

六、面试高频追问(附标准答案)

一、为什么 90% 的 UI 自动化脚本活不过 3 个月?

  • 元素定位"玄学"------今天跑通、明天 404

  • 等待策略"拍脑袋"------隐式、显式、sleep 乱用一通

  • 验证码、iframe、多窗口、动态 ID......一步一坑

本文主要关于常见Selenium应用API和常见问题处理。


二、Selenium必会 API 速查

  • 定位元素

    • element = 浏览器对象.find_element(by=By.定位方式,value="属性值")
  • 常见操作

    • 模拟点击:element.click()

    • 模拟输入:element.send_keys()

    • 模拟清除:element.clear()

  • 浏览器操作

    • 浏览器最大化driver.maximize_window()

    • 浏览器刷新driver.refresh()

    • 浏览器前进:driver.forward()

    • 浏览器后退:driver.back()

    • 获取标题:driver.title

    • 获取网页地址:driver.current_url

  • 页面元素判断

    • 元素是否可见:element.is_displayed()

    • 元素是否可用:element.is_enabled()

    • 元素是否选中element.is_selected ()

  • 滚动条处理

    • 定义js字符串:js = "window.scrollTo(0,1000)"

    • 执行js字符串:driver.execute_script(js)

  • 弹出框处理

    • 自定义弹框:通过元素定位后直接处理

    • JS弹框

      • alert:告警框

      • confirm:确认框

      • prompt:普通提示框

      alert= driver.switch_to.alert #获取弹出框对象,三种弹出框,在对象获取的时候都一样
      alert.text #获取弹出框文本
      alert.accept() #接受弹出框,弹出框处理方法
      alert.dismiss() #取消弹出框,弹出框处理方法,确认框没有取消按钮,取消方法一样生效

  • 鼠标操作

    #导包
    from selenium.webdriver import Actionchains

    #实例化鼠标对象
    action = ActionChains(driver)

    #调用鼠标方法,element表示元素对象
    action.move_to_element(element) #鼠标悬停

    action.context_click(element) #鼠标右击

    action.double_click(element) #鼠标双击

    action.drag_and_drop(source, target) #拖拽

    #执行鼠标操作,调用鼠标方法并不会去执行鼠标操作,必须调用perform才会执行
    action.perform()

  • 下拉框操作

    Select类:只适用于HTML原生态的下拉框,即<select>+<option>的组合标签

    复制代码
    #1.导包
    from selenium.webdriver.support.select import Select
    
    #2.创建select对象
    select = Select(element)
    
    #3.选择选项
    select.select_by_index(index) #根据下标
    
    select.select_by_value(value) #根据选项value属性值
    
    select.select_by_visible_text(text) #根据选项文本

三、实践

场景 推荐写法(Selenium 4 新语法) 一句话提醒
元素定位 driver.find_element(By.CSS_SELECTOR, "#username") 拒绝 find_element_by_* 老语法,PyCharm 已标黄
等待元素可见 WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, "//button[text()='立即购买']"))) 显式等待优先,隐式等待只做"兜底"
下拉框 Select(driver.find_element(By.ID, "city")).select_by_visible_text("上海") 仅适用于原生 <select>,自定义组件请用点击组合
鼠标悬停 ActionChains(driver).move_to_element(menu).perform() 99% 的"悬浮才显示"坑都能用这一行解决
滚动到元素 driver.execute_script("arguments[0].scrollIntoView({block:'center'});", element) 滚动到中心,避免被固定 header 遮挡
新窗口 driver.switch_to.window(driver.window_handles[-1]) 句柄按出现顺序压栈,-1 永远是最新窗口
frame 切回 driver.switch_to.default_content() 嵌套 frame 请一层一层切,别"跳级"
截图取证 driver.save_screenshot(f"./evidence/{case_name}_{timestamp}.png") 目录提前建好,png 无损且 Jenkins 可预览
高亮标记 driver.execute_script("arguments[0].setAttribute('style', 'border:3px solid red');", elem) 失败截图前高亮,一眼看出哪个元素出错

四、10 大高频异常"症状 → 病因 → 处方"

症状 病因定位清单(按概率排序) 处方(复制即用)
NoSuchElementException 1. 动态 ID / 随机 class 1. 改用 CSS "稳态"属性,[data-testid="loginBtn"]
ElementClickIntercepted 被蒙层/吐司/固定 header 遮挡 滚动到中心 + JS 点击:driver.execute_script("arguments[0].click()", elem)
StaleElementReference DOM 整片刷新(Vue/React) 重新定位 + 显式等待,禁止把 WebElement 当"长期变量"
TimeoutException 显式等待条件写错 EC.presence_of_element_located 还是 visibility_of_element_located?前者只判 DOM,后者判可视
验证码阻挡 公司无白名单 ① 万能验证码 8888 配置
文件上传弹窗 不是网页元素 直接 send_keys(绝对路径)<input type=file>,AutoIt 已过时
下载弹窗 Chrome 每次询问 启动参数加:prefs = {"download.default_directory": "/tmp", "download.prompt_for_download": False}
内存暴涨 未关窗口、日志堆积 每条用例结束 driver.quit() + 日志轮转
多线程串包 全局静态 driver threading.local() 或 pytest-xdist 的 --dist=loadgroup
360/安全软件拦截 WebDriver 被识别 加启动参数:--disable-blink-features=AutomationControlled + 替换 cdc_ 变量(Selenium-Stealth)

五、可复用的工具函数

复制代码
# utils/selenium_helper.py
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException

class SeleniumHelper:
    def __init__(self, driver):
        self.driver = driver

    def wait_and_click(self, locator: tuple, timeout=10):
        """等待元素可见并点击"""
        WebDriverWait(self.driver, timeout).until(EC.visibility_of_element_located(locator)).click()

    def wait_iframe_and_switch(self, iframe_locator: tuple, timeout=10):
        """等待 iframe 出现并切换进入"""
        WebDriverWait(self.driver, timeout).until(
            EC.frame_to_be_available_and_switch_to_it(iframe_locator)
        )

    def back_to_default_content(self):
        self.driver.switch_to.default_content()

    def upload_file(self, input_locator: tuple, file_path: str):
        """原生上传"""
        self.driver.find_element(*input_locator).send_keys(file_path)

    def set_cookies_from_dict(self, cookies: dict):
        """一键注入登录态,跳过验证码"""
        self.driver.get("https://xxx.com")  # 先访问同域空白页
        for k, v in cookies.items():
            self.driver.add_cookie({"name": k, "value": v})
        self.driver.refresh()

六、面试高频追问(附标准答案)

  1. 隐式等待与显式等待能否同时用?

    答:可以,但隐式等待会拖慢显式等待的轮询效率,Selenium 官方建议二选一;生产环境统一用显式等待。

  2. 如何判断元素是否在 iframe?

    答:Chrome DevTools → Elements → 搜索 //iframe,看目标节点是否被 <iframe> 包裹;脚本里捕获 NoSuchElementException 后重试 driver.switch_to.frame()

  3. 验证码到底要不要自动化?

    答:QA 职责是"测试业务主路径",不是"破解验证码"。优先让开发配置万能验证码或关闭验证码;次选注入 Cookie;OCR 识别成本最高,ROI 最低。

相关推荐
Lenskit3 小时前
使用pyspark对上百亿行的hive表生成稀疏向量
python·spark-ml·spark
掘金一周3 小时前
还在用html2canvas?介绍一个比它快100倍的截图神器!| 掘金一周 9.4
前端·人工智能
南北是北北3 小时前
为什么会出现有声无画/黑屏,以及如何避免与优化
前端·面试
小桥风满袖3 小时前
极简三分钟ES6 - let声明
前端·javascript
南北是北北3 小时前
VSync 是什么、ExoPlayer 怎么对齐 VSync 与音画同步、常见问题与调参要点
前端·面试
前端fighter3 小时前
前端开发中的模块化:从 CommonJS 到 ES6 模块
前端·javascript·面试
wordbaby3 小时前
Reducer 模式(Reducer Pattern)是什么?
前端
熊猫片沃子3 小时前
一文搞懂 Vue 模板语法:插值、指令与过滤器的正确用法
前端·vue.js
AryaNimbus3 小时前
你不知道的Cursor系列:使用 Cursor 不会这个超牛 MCP 还没用过吧!
前端·ai编程·cursor