浏览器操作作业 · 超时等待

页面加载与元素等待超时设置

首页 浏览器操作导航

页面导读 · 超时等待

sleep(最差)→ implicit(全局隐式)→ explicit(推荐显式)

学习目标
理解三种等待的区别和适用场景,能手写 wait_until(callable, timeout, poll)
核心 API
time.sleep() · implicitly_wait() · WebDriverWait().until() · expected_conditions
作业数量
4 题基础 · +1 进阶可选
难度:⭐⭐⭐⭐ 进阶
⚠️ 前置注意: 显式+隐式混用会叠加时间,显式 3s + 隐式 10s 元素不存在时实际等约 10 秒而非 3 秒
参数配置 打开关闭 页面导航 窗口控制 页面信息 元素交互 Cookie操作 截图保存 超时等待

操作 9:超时控制与等待机制

三种等待方式对比:强制 sleep(最烂)→ 隐式 implicitly_wait(全局)→ 显式 WebDriverWait+EC(最专业)。

💡 核心差异:sleep 硬等 2 秒 → implicitly_wait 最多等 10 秒、元素一出现就返回 → WebDriverWait+EC 条件满足立即返回,元素 3.5 秒出现就只等 3.5 秒,绝不浪费。
time.sleep(t)
强制硬等 t 秒,不管元素什么时候出现,页面快了浪费、慢了不够,生产代码禁止使用
❌ 最差
效率★☆☆☆☆
implicitly_wait(t)
全局生效,元素一出现立即返回,最长等 t 秒。能接受但不够灵活
⚠️ 可用
效率★★★☆☆
WebDriverWait(driver, t).until(...)
显式条件等待,设定最长等待 t 秒,条件满足立即返回,绝不浪费一秒。最推荐、最专业
✅ 推荐
效率★★★★★
0.00s
0s5s10s
👆 点击上方任意卡片开始模拟
被测页面
⏳ 等待目标按钮 ["登录 / 注册"] 出现中...
# ===== 三种等待方式对比 =====

# ❌ 方式 1:强制等待 —— 写死时间,页面快了浪费、慢了不够
import time
time.sleep(2)

# ⚠️ 方式 2:隐式等待 —— 全局生效,元素出现就返回,最长等 10 秒
driver.implicitly_wait(10)
# 之后所有 find_element 都自动应用
# 缺点:① 全局影响难控制 ② 不擅长等 URL / 标题 / 元素消失

# ✅ 方式 3:显式等待 —— 最灵活、最推荐
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, 10)   # 最多等 10 秒
el = wait.until(EC.presence_of_element_located((By.ID, "demoInput")))
el.click()

# 常用 EC 条件
# EC.presence_of_element_located     元素在 DOM 中
# EC.visibility_of_element_located   元素可见(宽高>0)
# EC.element_to_be_clickable         元素可点击
# EC.url_contains("xxx")             URL 包含
# EC.title_contains("xxx")           标题包含
# EC.alert_is_present()              出现弹窗
# EC.invisibility_of_element_located 元素消失

# ⚠️ 三种超时配置
driver.set_page_load_timeout(30)    # 页面加载超时
driver.set_script_timeout(20)       # JS 执行超时
driver.implicitly_wait(10)          # 隐式等待

🧰 作业素材区(提供实验环境,不给答案)

复制下方代码/HTML 到本地文件,用 Selenium 打开测试;或点 demo-card 的三种等待按钮自动记录数据到作业 1 对比表
📌 作业 1:等待耗时对比表 (点上面 demo-card 的 sleep / implicit / explicit 按钮 → 自动填入你自己的实验数据)
等待方式设定 t 秒实际耗时效率你的备注
👆 请点击上方 demo-card 的 sleep / implicit / explicit 按钮,结果将自动填入
📌 作业 2:手写 wait_until —— 你的实现(Python)
# 👇 在下方写出你的 wait_until 实现,Python 版(和 demo-card 演示的 JS 逻辑一致)
import time

def wait_until(callable, timeout=10, poll=0.5):
    # ===== 你来写 =====
    # 1. 记录 start 时间
    # 2. while 循环,每次 poll 秒
    # 3. callable() 返回真 → 立即 return True
    # 4. 超时 → raise TimeoutException("wait_until timeout")
    # 5. 用一个计数器变量记录总共轮询了多少次,最后 return 或打印
    pass  # ← 把这行换成你的代码

# ===== 测试用例(复制到本地运行)=====
def make_delayed_button(driver, delay=3):
    # 注入 JS,让一个按钮在 delay 秒后才出现在 DOM 中
    js = """
    var btn = document.createElement('button');
    btn.id = 'delayed-btn';
    btn.textContent = '我是延迟出现的按钮';
    btn.style.padding = '10px 20px'; btn.style.fontSize = '16px';
    document.body.appendChild(btn);
    setTimeout(function() { btn.disabled = false; btn.style.background = '#10b981'; }, delay*1000);
    """
    driver.execute_script(js, delay)

# 用法:
# driver.get('about:blank')
# make_delayed_button(driver, 3)
# wait_until(lambda: driver.find_element(By.ID, 'delayed-btn').is_enabled(), timeout=10, poll=0.2)
# print("总共轮询了多少次?")
在 demo-card 里模拟 callable 延迟 秒
📌 作业 3:EC 条件选型 —— 3 个测试页面 HTML(复制到 .html 文件本地打开)
📄 场景 ①:页面加载到一半元素还没出现 —— half_loaded.html
<!DOCTYPE html>
<html><head><title>half_loaded</title></head><body>
  <p>页面标题、导航栏、图片等都已经出现了...</p>
  <p>但登录按钮还没渲染出来,需要等待前端 JS 加载完。</p>
  <script>
    setTimeout(function(){
      var b = document.createElement('button');
      b.id = 'login-btn';
      b.textContent = '登录 / 注册';
      document.body.appendChild(b);
    }, 4000);  // 按钮 4 秒后才插入 DOM
  </script>
</body></html>
📄 场景 ②:元素加载了但被遮罩层盖住 —— covered.html
<!DOCTYPE html>
<html><head><title>covered</title>
  <style>
    #login-btn { padding:10px 20px; font-size:16px; margin-top:40px; }
    #mask { position:fixed; top:0; left:0; width:100%; height:100%;
            background:rgba(0,0,0,0.7); display:flex; align-items:center;
            justify-content:center; color:white; font-size:24px; z-index:99; }
    .btn-close { margin-top:20px; padding:8px 16px; font-size:14px; }
  </style>
</head><body>
  <button id="login-btn">登录 / 注册</button>
  <div id="mask">请先登录!
    <br>
    <button class="btn-close" onclick="document.getElementById('mask').remove()">关闭遮罩</button>
  </div>
</body></html>
📄 场景 ③:元素存在但宽高都是 0(hidden) —— hidden.html
<!DOCTYPE html>
<html><head><title>hidden</title></head><body>
  <button id="secret-btn" style="visibility:hidden; width:0; height:0;">隐藏的按钮</button>
  <p>按钮 DOM 存在,visibility:hidden,宽高都是 0。3 秒后变可见:</p>
  <script>
    setTimeout(function(){
      var b = document.getElementById('secret-btn');
      b.style.visibility = 'visible';
      b.style.width = 'auto'; b.style.height = 'auto';
      b.style.padding = '10px 20px'; b.style.fontSize = '16px';
      b.style.background = '#10b981';
    }, 3000);
  </script>
</body></html>
💡 你的任务:用 Selenium + EC 打开这三个页面,分别写出你认为最合适的等待条件(presence / visibility / clickable / 其他),并解释为什么。
📌 作业 4:超时三件套 —— 慢响应 Flask 后端 + 测试代码模板
🔧 后端:slow_server.py(pip install flask 后运行)
from flask import Flask, Response
import time

app = Flask(__name__)

@app.route('/')
def slow_home():
    time.sleep(5)  # 首页故意慢 5 秒 → 触发 page_load_timeout
    return '<html><body><h1>Slow Page</h1></body></html>'

@app.route('/fast')
def fast():
    # fast 页面加载完了但 JS 死循环 → 触发 script_timeout
    return '<html><body><h1>Fast Page</h1>' \
           '<script>while(true){}</script></body></html>'

@app.route('/never-appear')
def never_appear():
    # implicitly_wait 找不到元素 → 抛什么异常?
    return '<html><body><p>页面里永远不会出现 #target-btn</p></body></html>'

if __name__ == '__main__':
    app.run(port=5000, debug=False)
🚀 启动后端(2 步):
① 安装:pip install flask(只需装一次)
② 启动:打开一个终端窗口,cd 到 slow_server.py 所在目录,运行 python slow_server.py,看到 Running on http://127.0.0.1:5000 即启动成功。
💡 保持这个终端开着! 另开一个终端跑你的 Selenium 测试脚本。Ctrl+C 才会停掉后端。
🔧 你的测试脚本骨架(填完 try/except 观察异常类型)
from selenium import webdriver
from selenium.common.exceptions import TimeoutException, ScriptTimeoutException, NoSuchElementException

driver = webdriver.Chrome()
BASE = 'http://127.0.0.1:5000'

# ===== 实验 1:page_load_timeout =====
driver.set_page_load_timeout(3)  # 故意设小,首页要 5 秒才加载完
try:
    driver.get(BASE + '/')
except Exception as e:
    print("实验 1 异常类型:", type(e).__name__)  # ← 你观察到什么?

# ===== 实验 2:script_timeout =====
driver.set_script_timeout(2)
driver.get(BASE + '/fast')
try:
    driver.execute_script("while(true){}")
except Exception as e:
    print("实验 2 异常类型:", type(e).__name__)  # ← 你观察到什么?

# ===== 实验 3:implicitly_wait =====
driver.implicitly_wait(5)
driver.get(BASE + '/never-appear')
try:
    driver.find_element('id', 'target-btn')  # 永远找不到
except Exception as e:
    print("实验 3 异常类型:", type(e).__name__)  # ← 和前两个一样吗?

driver.quit()
📌 作业 5:隐式 + 显式混用陷阱(进阶 · 可选) —— 实验脚本
先跑一遍记录 elapsed_alone(只用显式),再跑一遍记录 elapsed_mixed(显式 + 隐式),对比两个时间的差。
# 实验:显式等待 3 秒 + 隐式等待 10 秒,元素永远不存在
# 显式等待实际会等多久?

import time
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException

driver = webdriver.Chrome()
driver.get('about:blank')

# ===== 第一轮:只用显式等待 =====
start = time.perf_counter()
try:
    WebDriverWait(driver, 3).until(
        EC.presence_of_element_located(('id', 'never-exist'))
    )
except TimeoutException:
    pass
elapsed_alone = time.perf_counter() - start
print(f"单用显式等待耗时:{elapsed_alone:.2f}s")  # ← 你的答案:______ 秒

# ===== 第二轮:先设隐式等待,再用显式等待 =====
driver.implicitly_wait(10)  # ← 加了这一行
start = time.perf_counter()
try:
    WebDriverWait(driver, 3).until(
        EC.presence_of_element_located(('id', 'never-exist'))
    )
except TimeoutException:
    pass
elapsed_mixed = time.perf_counter() - start
print(f"显式 + 隐式混用耗时:{elapsed_mixed:.2f}s")  # ← 你的答案:______ 秒

print(f"差值:{elapsed_mixed - elapsed_alone:.2f}s")
# 混用后显式等待的实际时间变 ______ 了?

driver.quit()

📝 作业要求

  1. 写三段代码分别用 time.sleep(t) / implicitly_wait(t) / WebDriverWait(driver,t).until(...) 去等一个按钮出现,用 time.perf_counter() 精确统计各自花了多少秒,对比三者差异
  2. 实现一个自己的 wait_until(callable, timeout=10, poll=0.5) 函数:每 poll 秒轮询 callable() 返回值,为真立即返回;超时抛 TimeoutException;打印总等待了多少次
  3. 场景题:页面加载了一半时元素还没出现,应该用哪种等待?元素加载出来了但被其他 DOM 盖住了(click 报 ElementClickIntercepted),EC 用哪个条件最合适?元素加载了但宽高都是 0(hidden),presence 和 visibility 用哪个?
  4. 超时三件套实验:打开一个故意慢响应的页面,依次调 driver.set_page_load_timeout(3) / driver.set_script_timeout(2) / driver.implicitly_wait(5),分别观察超时抛的是什么异常(TimeoutException / ScriptTimeoutException)
  5. 进阶 · 可选 隐式 + 显式混用陷阱:先 implicitly_wait(10),再写一个 WebDriverWait(driver, 3).until(...) —— 如果元素不存在,显式等待实际会等 3 秒还是 10 秒?验证你的猜想
💡 提示:生产代码里 禁止 time.sleep(),全用 WebDriverWait + EC;隐式等待和显式等待不要混用,容易产生"显式等待不生效"的错觉

操作日志

下面会实时记录你在 9 个章节里的每一次操作。

⏳ 暂无任何操作,快去上面试试吧~

关于我们

Python 自动化测试学习平台是一个面向软件测试工程师、开发人员及在校学生的交互式在线学习平台,专注于 Selenium WebDriver 与 Python unittest 自动化测试技能的教学与实践。

我们以"理论 + 实战 + 可交互演示"为核心特色,将八种常规元素定位和Javascript定位、元素操作、自动化测试工具、单元测试、测试模型等知识体系拆分为模块化课程,每个模块都配有可点击、可输入、可运行的模拟练习页面。

平台致力于降低自动化测试的入门门槛,让学习者从"看懂代码"到"动手实操",逐步掌握企业级自动化测试框架的设计与落地能力。

联系方式

📧邮箱:lq@selenium-auto-test.com

📞电话:0755-12345678

📍地址:深圳

💬微信公众号:Selenium自动化测试

🐙GitHub:github.com/selenium-auto-test

免责声明

1. 本平台所有示例代码、演示页面及教程内容均仅供学习交流使用,不构成任何商业推广或技术服务承诺。

2. 平台中的 Selenium API 描述、Python 语法说明均以官方文档为准,如有版本差异请以对应官方文档为准。

3. 学习者在实际项目中应用平台所学内容时,应结合自身项目环境进行充分测试,本平台不对因直接使用示例代码而产生的任何直接或间接损失承担责任。

4. 本平台所引用的第三方库(Selenium、ChromeDriver、unittest 等)版权归其各自作者及维护者所有。

5. 如涉及版权问题或内容建议,请及时通过联系方式与我们沟通,我们将第一时间处理。