Selenium 等待机制
Selenium 等待机制全攻略(2025 最新版)
等待 = 自动化成功的命脉
没有正确的等待,99% 的脚本会失败!
掌握 隐式 / 显式 / 强制 / 智能等待,让脚本 稳定如狗
一、为什么需要等待?
| 问题 | 原因 |
|---|---|
NoSuchElementException | 元素还没加载 |
ElementNotInteractable | 元素存在但不可点击 |
StaleElementReference | 页面刷新,元素“过时” |
网页是动态的,JS 异步加载 → 必须等!
二、三大等待机制对比
| 类型 | 机制 | 推荐度 | 说明 |
|---|---|---|---|
| 强制等待 | time.sleep(3) | 不推荐 | 固定时间,太慢或太快 |
| 隐式等待 | driver.implicitly_wait(10) | 一般 | 全局生效,找元素时等 |
| 显式等待 | WebDriverWait(...).until() | 强烈推荐 | 精准、灵活、可定制 |
三、强制等待(time.sleep)—— 仅用于调试
import time
time.sleep(3) # 强制等 3 秒
缺点:
- 太慢 → 浪费时间
- 太快 → 还是会失败
- 无法应对动态加载
仅用于:临时调试、截图间隔
四、隐式等待(implicitly_wait)—— 全局等
driver.implicitly_wait(10) # 全局:每次 find_element 最多等 10 秒
工作原理:
- 每次
find_element/find_elements时生效 - 轮询查找,直到找到或超时
- 对所有元素生效
driver.implicitly_wait(10)
driver.find_element(By.ID, "kw") # 最多等 10 秒
driver.find_element(By.ID, "su") # 再次等 10 秒
优点:
- 一行代码,全局生效
- 适合简单脚本
缺点:
- 无法等待“可点击”或“可见”
- 容易掩盖问题
- 与显式等待混用会混乱
建议:新手可用,进阶禁用
五、显式等待(WebDriverWait)—— 王者之选
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
wait = WebDriverWait(driver, timeout=10, poll_frequency=0.5)
element = wait.until(
EC.presence_of_element_located((By.ID, "result"))
)
核心参数:
| 参数 | 说明 |
|---|---|
timeout | 最多等多久(秒) |
poll_frequency | 轮询间隔(默认 0.5s) |
ignored_exceptions | 忽略某些异常 |
六、Expected Conditions(EC)大全(必背 10 个)
| 条件 | 方法 | 说明 |
|---|---|---|
| 元素存在 | presence_of_element_located | DOM 中存在 |
| 元素可见 | visibility_of_element_located | 存在 + 可见 |
| 元素可点击 | element_to_be_clickable | 最常用! |
| 标题包含 | title_contains("百度") | 页面标题 |
| 标题是 | title_is("百度一下") | 精确标题 |
| URL 变化 | url_to_be("https://...") | URL 跳转 |
| 元素文本包含 | text_to_be_present_in_element | 文本出现 |
| 元素消失 | invisibility_of_element_located | 加载动画消失 |
| iframe 可切换 | frame_to_be_available_and_switch_to_it | iframe 加载 |
| alert 出现 | alert_is_present() | 弹窗 |
# 推荐组合:等待可点击
btn = wait.until(EC.element_to_be_clickable((By.ID, "su")))
btn.click()
七、实战示例:百度搜索(完整等待)
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 webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
wait = WebDriverWait(driver, 15)
try:
driver.get("https://www.baidu.com")
# 1. 等待搜索框出现并输入
search_box = wait.until(
EC.presence_of_element_located((By.ID, "kw"))
)
search_box.send_keys("Selenium 等待机制")
# 2. 等待按钮可点击并点击
search_btn = wait.until(
EC.element_to_be_clickable((By.ID, "su"))
)
search_btn.click()
# 3. 等待结果页标题包含关键词
wait.until(
EC.title_contains("Selenium 等待机制")
)
# 4. 等待搜索结果出现
wait.until(
EC.presence_of_element_located((By.ID, "content_left"))
)
print("搜索成功!标题:", driver.title)
finally:
driver.quit()
八、自定义等待条件(高级)
from selenium.webdriver.support import expected_conditions as EC
# 自定义:等待元素文本包含“成功”
def text_contains(driver, locator, text):
try:
elem = driver.find_element(*locator)
return text in elem.text
except:
return False
wait.until(lambda d: text_contains(d, (By.ID, "msg"), "登录成功"))
九、等待失败?自动截图 + 重试
def wait_and_click(driver, locator, timeout=10):
wait = WebDriverWait(driver, timeout)
try:
elem = wait.until(EC.element_to_be_clickable(locator))
elem.click()
except Exception as e:
driver.save_screenshot(f"error_{locator[1]}.png")
print("等待失败,已截图:", e)
raise
# 使用
wait_and_click(driver, (By.ID, "login"))
十、常见等待场景 & 最佳实践
| 场景 | 推荐等待 |
|---|---|
| 输入框 | presence_of_element_located |
| 按钮点击 | element_to_be_clickable |
| AJAX 加载 | invisibility_of_element_located(加载圈消失) |
| 页面跳转 | title_contains 或 url_contains |
| 下拉加载 | presence_of_element_located(新元素) |
| iframe | frame_to_be_available_and_switch_to_it |
# 等待 iframe 并切换
wait.until(EC.frame_to_be_available_and_switch_to_it((By.ID, "iframeResult")))
十一、等待策略金字塔
显式等待(WebDriverWait + EC)
↑
精准控制
↑
隐式等待(全局兜底)
↑
强制等待(仅调试)
最佳实践:
- 主用显式等待
- 禁用隐式等待(或设为 0)
- 从不用 time.sleep
driver.implicitly_wait(0) # 关闭隐式等待
十二、推荐工具
| 工具 | 用途 |
|---|---|
| SelectorsHub | 快速验证元素 |
| Chrome DevTools → Performance | 分析加载时间 |
| Playwright | 自动等待(参考) |
十三、一句话总结
显式等待 + element_to_be_clickable = 稳定脚本的灵魂
十四、练习网站
| 网站 | 练习点 |
|---|---|
| https://the-internet.herokuapp.com/dynamic_loading | AJAX 加载 |
| http://httpbin.org/delay/3 | 延迟响应 |
| https://kyfw.12306.cn | 复杂 iframe + 验证码 |
下一步:
想看 10 个真实网站等待案例(登录、翻页、无限加载、验证码)?
回复 wait_cases 我发你一个 完整项目模板!
终极等待模板(复制即用):
def wait_click(driver, locator, timeout=10):
WebDriverWait(driver, timeout).until(
EC.element_to_be_clickable(locator)
).click()
def wait_send_keys(driver, locator, text, timeout=10):
elem = WebDriverWait(driver, timeout).until(
EC.presence_of_element_located(locator)
)
elem.clear()
elem.send_keys(text)
wait_send_keys(driver, (By.ID, "kw"), "Selenium")
wait_click(driver, (By.ID, "su"))
现在,你的脚本再也不会“找不到元素”了!