1. 元素本身属性操作
获取元素状态或内容,用于断言或逻辑判断。
# 假设已定位到元素: element = driver.find_element(By.ID, "xxx")
# 1. 获取元素文本 (获取标签对之间的文本,不含 HTML 标签)
text_content = element.text
# 2. 判断元素是否被选中 (常用于单选框 radio、复选框 checkbox)
is_checked = element.is_selected() # 返回 True/False
# 3. 判断元素是否可用 (未被 disabled 禁用)
is_usable = element.is_enabled() # 返回 True/False
# 4. 判断元素是否可见 (在页面上显示且宽高不为0)
is_visible = element.is_displayed() # 返回 True/False
模拟点击:element.click()
模拟输入:element.send_keys()
模拟清除:element.clear()
页面刷新:element.refresh()
2. 浏览器窗口操作
控制浏览器窗口的行为和生命周期。
# 1. 最大化窗口
driver.maximize_window()
# 2. 刷新页面
driver.refresh()
# 3. 后退 (相当于点击浏览器后退按钮)
driver.back()
# 4. 前进 (相当于点击浏览器前进按钮)
driver.forward()
# 5. 退出 (关闭所有关联窗口并结束驱动进程,务必在脚本末尾使用)
driver.quit()
# 注意: driver.close() 仅关闭当前焦点窗口
3. 滚动条处理 (JavaScript 执行)
Selenium 无法直接操作滚动条,需通过执行 JS 代码实现。
# 1. 定义 JS 字符串
# 方式 A: 滚动到绝对坐标 (x=横向, y=纵向)
js_scroll_abs = "window.scrollTo(0, 500)"
# 方式 B: 滚动到底部
js_scroll_bottom = "window.scrollTo(0, document.body.scrollHeight)"
# 方式 C: 滚动到特定元素位置 (需先定位元素)
# element = driver.find_element(By.ID, "footer")
# js_scroll_elem = "arguments[0].scrollIntoView();"
# 2. 执行 JS 代码
driver.execute_script(js_scroll_abs)
4. 截图处理
用于自动化测试失败时的现场保留或验证。
# 截取整个页面并保存为文件
# 参数为文件路径 (支持 .png)
driver.get_screenshot_as_file("./screenshots/error_page.png")
# 或者获取二进制数据 (不保存文件,用于内存处理)
# img_data = driver.get_screenshot_as_png()
5. 多窗口/标签页切换
当点击链接打开新标签页时,需切换句柄(Handle)才能操作新窗口。
# 1. 获取所有窗口句柄 (列表形式)
handles = driver.window_handles
# 2. 切换到指定窗口 (通过下标或具体句柄值)
# 切换到最新打开的窗口 (通常是列表最后一个)
driver.switch_to.window(handles[-1])
# 切换到第一个窗口
driver.switch_to.window(handles[0])
# 3. (可选) 关闭当前窗口后切回主窗口
# driver.close()
# driver.switch_to.window(driver.window_handles[0])
6. Frame/Iframe 切换
若元素位于 <iframe> 或 <frame> 内部,必须先切换进入,操作完后再切出。
# 1. 定位到 frame 元素
frame_element = driver.find_element(By.ID, "login_frame")
# 或者直接通过 ID/Name 字符串: driver.switch_to.frame("login_frame")
# 2. 切换到该 frame (之后只能操作 frame 内部的元素)
driver.switch_to.frame(frame_element)
# ... 执行内部元素操作 ...
# 3. 切回主文档 (默认内容),以便操作外部元素
driver.switch_to.default_content()
# (可选) 切换到父级 frame (若嵌套多层)
# driver.switch_to.parent_frame()
7. 时间等待机制
解决页面加载慢导致的 NoSuchElementException。
A. 强制等待 (不推荐作为主要手段)
import time
# 程序暂停执行指定秒数,无论页面是否加载完成
time.sleep(5)
B. 隐式等待 (全局设置)
# 设置全局超时时间。若元素未立即出现,轮询查找直到超时。
# 只需设置一次,对后续所有 find_element 生效。
driver.implicitly_wait(10)
C. 显式等待 (推荐,局部精准控制)
配合 WebDriverWait 和 expected_conditions (EC) 使用。
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
# 初始化等待对象:最大等待10秒,每0.5秒检查一次
wait = WebDriverWait(driver, 10, poll_frequency=0.5)
# 场景 1: 等待元素可见 (推荐,确保元素不仅能找到,还能交互)
element = wait.until(
EC.visibility_of_element_located((By.ID, "username"))
)
# 场景 2: 等待元素存在 (仅在 DOM 中,不一定可见)
element = wait.until(
EC.presence_of_element_located((By.ID, "username"))
)
# 场景 3: 等待元素可点击
element = wait.until(
EC.element_to_be_clickable((By.ID, "submit_btn"))
)
# 场景 4: 自定义条件 (Lambda 表达式)
element = wait.until(
lambda x: x.find_element(By.ID, "dynamic_content")
)
8. 鼠标与键盘操作
用于处理悬停 (Hover)、拖拽 (Drag & Drop)、右键点击、双击等复杂交互。
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
# 1. 初始化动作链
actions = ActionChains(driver)
# 2. 鼠标悬停 (二级菜单常用)
# 先定位到父级菜单元素
menu_element = driver.find_element(By.ID, "menu_item")
actions.move_to_element(menu_element).perform() # .perform() 必须调用以执行动作
# action.perform() 执行鼠标动作
# 3. 右键点击
# actions.context_click(element).perform()
# 4. 双击
# actions.double_click(element).perform()
# 5. 拖拽 (从源元素拖到目标元素)
# source = driver.find_element(By.ID, "drag")
# target = driver.find_element(By.ID, "drop")
# actions.drag_and_drop(source, target).perform()
# 6. 键盘组合键 (如: Ctrl+A 全选, Ctrl+C 复制)
# input_box = driver.find_element(By.ID, "content")
# input_box.send_keys(Keys.CONTROL, "a") # 全选
# input_box.send_keys(Keys.CONTROL, "c") # 复制
9. 浏览器原生弹框处理
针对 window.alert, window.confirm, window.prompt 生成的原生弹窗,无法用普通元素定位。
from selenium.webdriver.common.alert import Alert
# 1. 切换到弹框
alert = driver.switch_to.alert
# 2. 获取弹框文本内容
alert_text = alert.text
# 3. 点击"确定" (接受)
alert.accept()
# 4. 点击"取消" (解散,仅适用于 confirm/prompt)
# alert.dismiss()
# 5. 在 prompt 弹框中输入内容
# alert.send_keys("输入的内容")
# alert.accept()
10. 下拉框处理 (Select)
针对标准的 <select> 标签,Selenium 提供了专用类 Select。
from selenium.webdriver.support.ui import Select
# 1. 定位到 select 元素
select_element = driver.find_element(By.ID, "province")
dropdown = Select(select_element)
# 2. 通过可见文本选择
dropdown.select_by_visible_text("北京市")
# 3. 通过 value 属性值选择
dropdown.select_by_value("BJ")
# 4. 通过索引选择 (从0开始)
dropdown.select_by_index(1)
# 5. 取消选择 (仅支持多选下拉框)
# dropdown.deselect_all()
# dropdown.deselect_by_visible_text("北京市")
# 6. 判断是否多选
is_multi = dropdown.is_multiple
11. 其他 JavaScript 高级用法
除了滚动条,JS 还可用于修改元素属性(绕过前端限制)或直接点击。
# 1. 移除元素的 disabled 属性 (强制启用按钮)
# btn = driver.find_element(By.ID, "submit_btn")
# driver.execute_script("arguments[0].removeAttribute('disabled')", btn)
# 2. 直接通过 JS 点击元素 (当 element.click() 被遮挡或失效时)
# driver.execute_script("arguments[0].click()", btn)
# 3. 修改输入框的值 (绕过某些监听事件限制)
# input_box = driver.find_element(By.ID, "username")
# driver.execute_script("arguments[0].value='admin'", input_box)
# 4. 获取页面标题或当前 URL (通常直接用 driver.title / driver.current_url,但也可用 JS)
# title = driver.execute_script("return document.title")
12. 定位不到元素?排查清单 (Troubleshooting) 🛠️
当报错 NoSuchElementException 或 ElementNotInteractableException 时,按此顺序检查:
| 序号 | 可能原因 | 解决方案 |
|---|---|---|
| 1 | 元素未加载 | 页面慢,元素还没出来。解决 :加显式等待 (WebDriverWait)。 |
| 2 | 在 Frame 里 | 元素在 <iframe> 内部,主文档找不到。解决 :switch_to.frame()。 |
| 3 | 在新窗口 | 点击后开了新标签页,焦点还在旧窗口。解决 :switch_to.window()。 |
| 4 | 需要悬停 | 元素是隐藏菜单,需鼠标移上去才显示。解决 :ActionChains.move_to_element()。 |
| 5 | 属性动态变化 | ID 或 Class 是随机生成的 (如 id="btn_123" )。解决:改用 XPath 模糊匹配或稳定属性。 |
| 6 | 元素被遮挡 | 有广告弹窗或透明层盖住了元素。解决 :先关闭弹窗,或用 execute_script 强制点击。 |
| 7 | 不在可视区 | 元素在屏幕下方,未滚动到。解决 :scrollIntoView 滚动。 |
| 8 | 非标准下拉框 | 不是 <select> 标签,而是 div+ul+li 模拟的。解决 :直接用普通元素定位点击,不要用 Select 类。 |
13. 验证码处理方案 (Captcha Handling) 🛡️
Selenium 无法直接识别图形验证码。根据项目阶段和验证码类型,通常采用以下 4 种策略:
方案 A:测试环境"万能码/后门" (⭐⭐⭐⭐⭐ 推荐)
场景 :公司内部测试环境。
做法 :联系开发人员,在测试环境设置一个"万能验证码"(如固定输入 8888 或 1234 即可通过),或者在代码中判断如果是测试账号则跳过验证逻辑。
优点 :最稳定、执行最快、维护成本最低。
缺点:需要开发配合,生产环境不可用。
# 示例:测试环境固定验证码
if env == "test":
code_input.send_keys("8888") # 开发约定的万能码
方案 B:Cookie 绕过法 (⭐⭐⭐⭐ 推荐)
场景 :登录一次后,后续操作不需要重复登录。
做法:
-
手动登录一次成功网站。
-
获取浏览器的
Cookie(特别是包含用户身份信息的 Session ID)。 -
在自动化脚本启动浏览器后,先注入该 Cookie,然后刷新页面,直接处于"已登录"状态,跳过验证码步骤。
优点 :无需识别验证码,模拟真实用户状态。
缺点:Cookie 有有效期,过期需重新获取;首次仍需人工或识别介入。1. 打开首页
driver.get("http://example.com")
2. 添加 Cookie (格式需与浏览器一致)
driver.add_cookie({"name": "SESSION_ID", "value": "abc123xyz...", "domain": "example.com"})
3. 刷新页面生效
driver.refresh()
此时已自动登录,可直接操作内部页面
方案 C:第三方打码平台 (⭐⭐⭐ 通用方案)
场景 :必须通过真实验证码,且无后门。
做法 :调用第三方 OCR 识别平台 API(如:超级鹰、云打码、2Captcha 等)。
流程 :截图验证码 -> 发送图片到平台 -> 平台返回识别结果 -> 填入输入框。
优点 :能解决大多数常见图形验证码。
缺点:收费、有网络延迟、识别率非 100%、依赖外部服务稳定性。
# 伪代码示例 (以超级鹰为例)
from chaojiying import ChaojiyingClient
# 1. 截取验证码元素图片
element = driver.find_element(By.ID, "captcha_img")
element.screenshot("./code.png")
# 2. 调用第三方 API
client = ChaojiyingClient('用户名', '密码', '软件ID')
result = client.post_file(9004, './code.png') # 9004代表验证码类型
# 3. 获取识别结果并输入
if result['ret'] == 0:
code = result['pic_str']
driver.find_element(By.ID, "captcha_input").send_keys(code)
else:
print("识别失败")
方案 D:本地 OCR 库 (⭐⭐ 简单验证码)
场景 :验证码非常简单(纯数字、无干扰线、字体清晰)。
做法 :使用 tesseract-ocr (Python 库 pytesseract) 本地识别。
优点 :免费、无需网络。
缺点:对复杂验证码(干扰线、扭曲、点选)识别率极低,配置环境麻烦。
import pytesseract
from PIL import Image
# 1. 截图
element.screenshot("./code.png")
# 2. 图片预处理 (去色、二值化等,提高识别率 - 需OpenCV配合)
# ... 预处理代码 ...
# 3. 识别
text = pytesseract.image_to_string(Image.open("./code.png"), lang='eng')
driver.find_element(By.ID, "captcha_input").send_keys(text.strip())
💡 核心建议:
- 首选:找开发加测试后门(方案 A)。
- 次选:Cookie 绕过(方案 B),适合登录后的大流程测试。
- 最后手段:第三方打码(方案 C),仅在必须验证登录功能且无其他办法时使用。
- 避免:不要试图用 Selenium 自己去"猜"或硬编码复杂的动态验证码。
💡 Selenium 核心操作与最佳实践终极速查表
| 场景分类 | 推荐方法 / 核心类 | 关键语法/注意点 | 🏆 最佳实践建议 |
|---|---|---|---|
| 基础交互 | click(), send_keys(), clear() |
输入前建议先 clear() 清空旧值 |
操作前先检查 is_displayed() 和 is_enabled(),确保元素可见且可用 |
| 状态断言 | element.text, is_selected() is_enabled(), is_displayed() |
text 仅获取可见文本; is_系列返回布尔值 |
不要只依赖 find_element 不报错,务必用 is_ 系列做逻辑判断 |
| 复杂鼠标 | ActionChains |
必须 调用 .perform() 才会执行动作 |
用于悬停 (Hover)、拖拽、右键;链式调用更清晰 |
| 原生弹窗 | driver.switch_to.alert |
必须先切换上下文才能操作 (.accept(), .dismiss()) |
仅处理浏览器原生 Alert/Confirm/Prompt,非原生模态框需用普通定位 |
| 下拉菜单 | Select 类 |
仅限 <select> 标签 (select_by_visible_text 等) |
若是 div+ul 模拟的下拉框,直接使用普通元素定位点击,勿用 Select 类 |
| 滚动/JS | driver.execute_script() |
JS 是操作滚动条、修改属性的唯一途径 | 优先用 scrollIntoView 滚动到元素;可用于移除 disabled 属性或强制点击 |
| Frame 切换 | switch_to.frame() switch_to.default_content() |
进得去必须出得来 | 操作完嵌套页面后,务必立即调用 default_content() 切回主文档,避免后续定位失败 |
| 多窗口 | window_handles switch_to.window() |
句柄是动态列表,需重新获取 | 切换到新窗口操作完后,若需回主窗口,记得再次切换句柄 |
| 等待机制 | WebDriverWait + EC |
拒绝 滥用 time.sleep() 慎用 全局隐式等待 |
首选显式等待 (精准控制),次选隐式等待,尽量避免强制等待以提高脚本稳定性 |
| 环境清理 | driver.quit() |
关闭所有窗口并结束驱动进程 | 务必放在 finally 块或 tearDown 方法中,确保即使报错也能执行资源释放 |