当前位置: 首页 > ops >正文

Selenium 等待机制:编写稳定可靠的自动化脚本

一、为什么需要等待机制?

网页是动态加载的,元素出现的时间不确定。如果脚本在元素还没加载完成时就尝试操作它,就会抛出 NoSuchElementException 异常。

三种等待方式:

  1. 强制等待time.sleep() - 简单但低效

  2. 隐式等待driver.implicitly_wait() - 全局设置

  3. 显式等待WebDriverWait + expected_conditions - 最推荐的方式


二、强制等待 (不推荐但需了解)

import timetime.sleep(5) # 强制等待5秒
  • 优点:简单易用

  • 缺点

    • 效率低下(总是等待固定时间)

    • 不可靠(网络慢时可能不够,网络快时浪费時間)


三、隐式等待 (Implicit Wait)

设置一个全局的等待时间,对所有元素查找操作都生效。

from selenium import webdriverdriver = webdriver.Chrome()
driver.implicitly_wait(10) # 设置隐式等待时间为10秒driver.get("https://example.com")
# 所有find_element操作都会最多等待10秒
element = driver.find_element(By.ID, "some-element")
  • 工作原理:在查找元素时,如果立即没找到,会轮询DOM直到找到元素或超时

  • 优点:一次设置,全局生效

  • 缺点

    • 不够灵活,无法针对特定条件等待

    • 可能会影响脚本性能


四、显式等待 (Explicit Wait) - 重点推荐

显式等待允许你设置特定条件,只在需要的地方等待,更加灵活和高效。

1. 基本用法

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By# 创建WebDriverWait实例,设置最长等待时间10秒
wait = WebDriverWait(driver, 10)# 等待直到元素可见
element = wait.until(EC.visibility_of_element_located((By.ID, "myDynamicElement")))# 等待直到元素可点击
element = wait.until(EC.element_to_be_clickable((By.ID, "submit-btn")))# 然后进行操作
element.click()

2. 常用的 Expected Conditions

Selenium 提供了许多预定义的条件,以下是最常用的几种:

元素存在与可见性
  • presence_of_element_located(locator) - 元素出现在DOM中(不一定可见)

  • visibility_of_element_located(locator) - 元素可见(有宽度和高度)

  • invisibility_of_element_located(locator) - 元素不可见或不存在

元素可交互性
  • element_to_be_clickable(locator) - 元素可见且可点击

  • element_to_be_selected(locator) - 复选框/单选框可被选中

文本内容
  • text_to_be_present_in_element(locator, text) - 元素包含特定文本

  • text_to_be_present_in_element_value(locator, text) - 元素的value属性包含特定文本

页面状态
  • title_is(title) - 页面标题完全匹配

  • title_contains(partial_title) - 页面标题包含特定文本

  • url_to_be(url) - URL完全匹配

  • url_contains(partial_url) - URL包含特定文本

元素选择状态
  • element_located_to_be_selected(locator) - 元素被选中

  • element_located_selection_state_to_be(locator, is_selected) - 元素选中状态符合预期

元素数量
  • presence_of_all_elements_located(locator) - 至少找到一个元素

  • number_of_elements_to_be(locator, number) - 找到特定数量的元素

  • number_of_elements_to_be_less_than(locator, number) - 元素数量少于指定值

  • number_of_elements_to_be_more_than(locator, number) - 元素数量多于指定值

3. 自定义等待条件

如果预定义条件不满足需求,你可以创建自定义等待条件:

from selenium.webdriver.support.ui import WebDriverWait# 自定义等待函数 - 等待元素包含特定类名
def element_has_class(locator, class_name):def predicate(driver):element = driver.find_element(*locator)if class_name in element.get_attribute("class").split():return elementreturn Falsereturn predicate# 使用自定义等待条件
wait = WebDriverWait(driver, 10)
element = wait.until(element_has_class((By.ID, "my-element"), "active"))

4. 高级用法:设置轮询频率和忽略异常

from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import NoSuchElementException# 设置等待时间10秒,每0.5秒检查一次,忽略NoSuchElementException异常
wait = WebDriverWait(driver, timeout=10, poll_frequency=0.5,ignored_exceptions=[NoSuchElementException]
)element = wait.until(EC.visibility_of_element_located((By.ID, "my-element")))

五、混合使用等待策略

最佳实践是结合使用隐式等待和显式等待:

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import Bydriver = webdriver.Chrome()
# 设置一个较短的隐式等待作为后备
driver.implicitly_wait(5)driver.get("https://example.com")try:# 使用显式等待处理关键元素wait = WebDriverWait(driver, 10)login_button = wait.until(EC.element_to_be_clickable((By.ID, "login-btn")))login_button.click()# 等待页面跳转完成(URL变化)wait.until(EC.url_contains("dashboard"))# 等待欢迎消息出现welcome_message = wait.until(EC.visibility_of_element_located((By.CLASS_NAME, "welcome-msg")))print("登录成功:" + welcome_message.text)finally:driver.quit()

六、实战示例:处理动态加载内容

假设有一个页面,点击按钮后通过AJAX加载内容:

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keysdriver = webdriver.Chrome()
wait = WebDriverWait(driver, 10)driver.get("https://example.com/dynamic-content")# 点击加载更多按钮
load_more_btn = wait.until(EC.element_to_be_clickable((By.ID, "load-more")))
load_more_btn.click()# 等待新内容加载完成(等待特定元素出现)
# 方法1:等待新增的元素
new_item = wait.until(EC.presence_of_element_located((By.XPATH, "//div[@class='item'][last()]"))
)# 方法2:等待加载指示器消失
wait.until(EC.invisibility_of_element_located((By.ID, "loading-spinner"))
)# 方法3:等待特定数量的元素
wait.until(EC.number_of_elements_to_be((By.CLASS_NAME, "item"), 10)
)print("新内容加载完成!")
driver.quit()

七、常见问题与解决方案

1. 等待超时怎么办?

from selenium.common.exceptions import TimeoutExceptiontry:element = wait.until(EC.visibility_of_element_located((By.ID, "slow-element")))
except TimeoutException:print("元素加载超时,执行备用方案")# 执行其他操作或重新加载页面driver.refresh()# 再次尝试等待element = wait.until(EC.visibility_of_element_located((By.ID, "slow-element")))

2. 处理StaleElementReferenceException

当元素不再附加到DOM时会发生此异常,常见于页面刷新或AJAX更新后:

from selenium.common.exceptions import StaleElementReferenceExceptiondef wait_for_non_stale_element(locator, timeout=10):wait = WebDriverWait(driver, timeout)return wait.until(lambda d: d.find_element(*locator))element = wait_for_non_stale_element((By.ID, "dynamic-element"))

3. 等待多个条件

# 使用lambda表达式组合多个条件
wait.until(lambda d: d.find_element(By.ID, "element1").is_displayed() and d.find_element(By.ID, "element2").is_enabled()
)

八、最佳实践总结

  1. 优先使用显式等待:针对特定条件等待,更加精确和高效

  2. 合理设置超时时间:根据网络速度和页面复杂度设置

  3. 使用合适的预期条件:根据具体需求选择最匹配的条件

  4. 避免混合使用隐式和显式等待:可能导致不可预测的等待时间

  5. 处理异常:使用try-except块处理可能的超时异常

  6. 编写自定义等待条件:当内置条件不满足需求时

http://www.xdnf.cn/news/19237.html

相关文章:

  • Kubernetes中kubeadm、kubectl、kubelet的区别与作用
  • 动态规划入门(三):一些经典动态规划模型
  • arnold图像加密(猫脸变换)
  • 一个从7zip中分离出来的高压缩比文本压缩工具ppmd
  • 文件系统深度解析:从核心概念到代码实践
  • 【MLLM】多模态理解Ovis2.5模型和训练流程(更新中)
  • 手写MyBatis第43弹:插件拦截原理与四大可拦截对象详解
  • Shell脚本编程入门:从基础语法到流程控制
  • USB4 vs USB3.0:一场接口技术的革命性飞跃
  • 鸿蒙ArkTS 核心篇-14-条件表达式(三目运算符)
  • 如何提高微型导轨的生产效率?
  • 使用 Visio Viewer 查看 Visio 绘图文件
  • 语义分割一站式到底怎么玩?
  • 中级统计师-统计实务-第三章 国民经济核算
  • 智能装备如何与软件结合?
  • MySQL独占间隙锁为什么会互相兼容?
  • 慢SQL优化
  • SQL 学习
  • 以声为剑,绘山河热血——刘洋洋《不惧》8月30日全网上线
  • 逆向思维下,如何把基金投资做亏?
  • 算法 --- 前缀和
  • 一文了解大模型微调
  • AWD相关知识
  • 【Python】国内可用的高速pip镜像源大全
  • 蓝牙5.3核心技术架构解析:从控制器到主机的无线通信设计
  • 知识随记-----Qt 样式表深度解析:何时需要重写 paintEvent 让 QSS 生效
  • 鸿蒙ArkTS 核心篇-15-条件渲染(组件)
  • 如何改变传统教育的消费习惯-第三代结束-第四代开启
  • 源码解析-时间轮[HashedWheelTimer]
  • 项目管理方法如何选择