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

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.dtcms.com/a/360509.html

相关文章:

  • spi总线
  • 7.2elementplus的表单布局与模式
  • MCP SDK 学习二
  • 艾体宝案例 | 数据驱动破局:DOMO 如何重塑宠物零售门店的生存法则
  • Python 2025:AI代理、Rust与异步编程的新时代
  • 张柏芝亮相林家谦演唱会 再次演绎《任何天气》
  • Spring MVC 九大组件源码深度剖析(五):HandlerAdapter - 处理器的执行引擎
  • 三、环境搭建之Docker安装mysql
  • 一、计算机系统知识
  • Springcloud-----Nacos
  • 【influxdb】InfluxDB 2.x 线性写入详解
  • 层次分析法
  • Redis实现短信登录
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘SQLModel’问题
  • 37. 解数独
  • 解锁Tensor Core性能:深入探索CUDA Warp矩阵操作
  • Dify构建AI应用
  • FART 主动调用组件深度解析:破解 ART 下函数抽取壳的终极武器
  • #Datawhale 组队学习#8月-工作流自动化n8n入门-3
  • 第七章 使用角色和Asible内容集合简化Playbook
  • 4.4 光照(4) - 高光反射
  • 硬件工程师成长之路:从入门到精通的技术旅程
  • [Plecs基础知识系列]建立自定义模块/子系统(Subsystem)
  • C++ 面试高频考点 力扣 69. x 的平方根 二分查找 题解 每日一题
  • Linux网络socket套接字(中)
  • 切片语法[::-1]及其可用的类型
  • 基于单片机智能鞋柜/智能鞋橱/智能鞋盒
  • Linux - #操作系统概念 #权限
  • 获取某天的零点日期
  • Java 异常处理全解析:从基础到实践