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()
# 页面刷新:driver.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() 必须调用以执行动作
# 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 方法中,确保即使报错也能执行资源释放 |
