目录
一、为什么 90% 的 UI 自动化脚本活不过 3 个月?
二、Selenium必会 API 速查
三、实践
四、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 TimeoutExceptionclass SeleniumHelper:def __init__(self, driver):self.driver = driverdef 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()
六、面试高频追问(附标准答案)
-
隐式等待与显式等待能否同时用?
答:可以,但隐式等待会拖慢显式等待的轮询效率,Selenium 官方建议二选一;生产环境统一用显式等待。 -
如何判断元素是否在 iframe?
答:Chrome DevTools → Elements → 搜索//iframe
,看目标节点是否被<iframe>
包裹;脚本里捕获NoSuchElementException
后重试driver.switch_to.frame()
。 -
验证码到底要不要自动化?
答:QA 职责是“测试业务主路径”,不是“破解验证码”。优先让开发配置万能验证码或关闭验证码;次选注入 Cookie;OCR 识别成本最高,ROI 最低。