浏览器操作演示 · 超时等待

设置超时时间与等待策略

首页 浏览器操作导航

本页说明 —— 超时控制与等待机制

Selenium 稳定性的 核心专题:80% 的"偶发失败"都来自等待问题。搞清楚三层超时配置 + 三种等待方式的区别,是从入门到熟练的分水岭。

三层超时配置:

三种等待对比:

💡 最佳实践:默认只设一个较短的 implicitly_wait(1),复杂场景用 WebDriverWait 显式等待。永远不要混用长隐式 + 短显式!

超时与等待

超时控制与等待机制

page_load_timeout / script_timeout / implicitly_wait / WebDriverWait / time.sleep

超时控制(Timeouts)

page_load_timeout / script_timeout / implicitly_wait
什么是超时?—— "上限策略,防卡死"
Selenium 脚本执行速度快,但网页加载慢、网络不稳定、服务器偶发宕机。如果不设超时,默认等 300 秒(5 分钟)才会放弃——一个死链就会把整个测试套件拖死。
超时的本质: 设上限,过了就放弃 防脚本被卡死拖死 正常页面不受影响
⚠️ 核心认知:超时设 30s,页面 5s 正常加载完 → 还是 5s 返回。超时只影响"超过时间还没完成"的异常情况,对正常页面没有任何性能损失。
Selenium 三种超时完整对比
超时类型 方法调用 API(Selenium 3/4 通用) 属性赋值 API(Selenium 4+ 推荐) 等什么 谁触发 初始值
页面加载超时driver.set_page_load_timeout(30)driver.timeouts.page_load = 30等 readyState = "complete" get / back / forward / refresh 300s
脚本执行超时driver.set_script_timeout(10)driver.timeouts.script = 10等 JS 脚本执行完毕 execute_script / execute_async_script 30s
隐式等待超时driver.implicitly_wait(3)driver.timeouts.implicit_wait = 3等元素出现在 DOM 中 find_element / find_elements 0s(关闭)
Selenium 4 统一写法(推荐):
from selenium.webdriver.common.timeouts import Timeouts
driver.timeouts = Timeouts(implicit_wait=3, page_load=30, script=10)
✅ 既能赋值,也能读值(旧写法做不到): print(driver.timeouts.page_load) → 返回当前 page_load 值
怎么设合理的超时值?
方法:找一台能稳定访问的电脑,刷新目标页面 10 次,取其中第 9 快的那个时间(P90),再加 5~10 秒缓冲。
网站类型 page_load_timeout 建议值 script_timeout 建议值 隐式等待建议值
公开官网 / 博客 / API 文档10 ~ 15 秒5 秒1 ~ 2 秒
一般业务系统(OA、CRM)20 ~ 30 秒10 秒2 ~ 3 秒
中后台管理系统(有图表)45 ~ 60 秒15 秒2 ~ 3 秒
内部系统(VPN/内网)60 ~ 90 秒20 秒3 秒
大数据看板 / BI 报表90 ~ 120 秒30 秒3 秒
⚠️ 超过 120 秒还加载不完的页面,不是该调大超时,是该优化系统(前端代码、后端接口、数据库查询)。
超时后怎么办?—— window.stop() + 重试 / 跳过 / 截图留证
⚠️ 超时时绝不要在"半加载的坏页面"上继续定位元素,大概率找不到或找错。正确做法是:
① window.stop()
把浏览器从"半卡半死的 loading"拉回来,让后续 get() 能正常执行。
driver.execute_script("window.stop()")
② 截图留证
自动化测试中超时务必截图,Allure/pytest-html 会自动附到报告上。
driver.save_screenshot("timeout.png")
③ 重试 / 跳过
重试几次还不行就放弃,不要让一个失败拖住整个脚本。
# 生产级 safe_get 重试模板
from selenium.common.exceptions import TimeoutException

def safe_get(driver, url, retries=3):
    for attempt in range(1, retries + 1):
        try:
            driver.get(url)
            return True # ✅ 成功
        except TimeoutException:
            driver.execute_script("window.stop()") # 救回浏览器
            driver.delete_all_cookies()
            driver.save_screenshot(f"timeout_attempt{attempt}.png")
    return False # ❌ 彻底放弃
设置超时的影响
about:blank
📋 Dashboard
Loading...
Loading...
Loading...
加载中...
加载中...
#
Name
Status
1
Task Alpha
...
2
Task Beta
...
readyState: — ⏱ 0.00s
演示统计
选择下方按钮开始演示
观察超时机制的效果差异
「超时」设置
s
s
s
page_load
script
implicit
全部应用
当前演示区配置:未设置
选择超时场景模拟效果
演示重点: page_load_timeout 管页面加载, script_timeout 管 execute_script, implicitly_wait 管 find_element。
💡 超时设置建议
✅ page_load_timeout 建议设 30s —— WebDriver session 创建后初始值为 300s(5 分钟,W3C 规范规定)
    └ 一个页面资源卡住就白等 5 分钟,整个测试套件被拖慢数倍
❌ 不调用 set_page_load_timeout() 就会一直用页面加载超时初始值 300s(太长,正常页面加载用不了这么久)
✅ WebDriver session 创建后统一设一次超时,后面所有 get() 自动生效
❌ 避免每个 get() 前都重新 set_page_load_timeout —— 既冗余又容易漏改
✅ TimeoutException 异常完整处置流程:捕获 → driver.execute_script("window.stop()") → 重试/跳过/截图留证
❌ 只 catch 不 stop(浏览器仍 loading,后续操作不可控);或想让测试 fail-fast 时瞎 catch(超时不暴露问题)
超时演示页面
🔐 示例网站登录
📖 演示说明
在左侧表单输入用户名和密码,点击「登录」即可看到结果。模拟三层超时代码中 driver.find_element("id", ...) 的定位流程。
⏱ 三层超时分别控制哪行代码?
page_load=30s → driver.get(".../login") ← 页面导航最长等 30s
script=10s → driver.execute_script("...") ← 同步 JS 执行最长等 10s
implicit_wait=1s → find_element("id", "username") ← 找不到元素时重试最多 1s
implicit_wait=1s → find_element("id", "password") ← 同上
implicit_wait=1s → find_element("id", "login-btn") ← 同上
implicit_wait=1s → find_element("id", "page-title") ← 同上
💡 核心教学点
这是 三层超时配置 的配套 demo——没有显式等待(WebDriverWait),
全靠 page_load=30s + script=10s + implicit_wait=1s 三层超时兜底。
适合页面结构简单、不需要精确等待的场景。
最佳实践:三层超时配置(生产代码推荐写法)
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.timeouts import Timeouts
from selenium.common.exceptions import TimeoutException

# ═══════════════════════════════════════
# 初始化:Selenium 4 三层超时统一配置
# ═══════════════════════════════════════
options = Options()
options.add_argument("--headless=new")
driver = webdriver.Chrome(options=options)

# ─── 方案一:Selenium 4 新写法(推荐,Pythonic) ───
driver.timeouts = Timeouts(
    page_load=30, # 第 1 层:页面导航上限 30s(必设)
    script=10, # 第 2 层:JS 执行上限 10s(必设)
    implicit_wait=1 # 第 3 层:元素查找兜底 1s(极短兜底)
)

# ─── 方案二:传统 API 写法(效果相同,逐行设置) ───
# driver.set_page_load_timeout(30) # 第 1 层
# driver.set_script_timeout(10) # 第 2 层
# driver.implicitly_wait(1) # 第 3 层

# ═══════════════════════════════════════
# 执行:基本业务流程
# ═══════════════════════════════════════
try:
    driver.get("https://example.com/login")

    driver.execute_script("document.title = '登录中...'") # 受控于 script=10s

    driver.find_element("id", "username").send_keys("admin")
    driver.find_element("id", "password").send_keys("secret")
    driver.find_element("id", "login-btn").click()

    title = driver.find_element("id", "page-title").text
    print(f"✅ 登录成功:{title}")
except TimeoutException as e:
    driver.execute_script("window.stop()")
    driver.save_screenshot("fail.png")
    print(f"❌ 超时:{e}")
finally:
    driver.quit()

等待机制(Waits)

time.sleep / implicitly_wait / WebDriverWait + expected_conditions
什么是等待?—— "积极轮询,等条件变好"
和超时的被动"设上限、过了就放弃"完全不同,等待是主动的"我愿意花时间等条件满足"。Selenium 能做的唯一方式是 轮询(浏览器无法主动推事件给 Python)。
三个概念一句话总结: sleep = 硬等,不管页面 implicitly_wait = 全局兜底 WebDriverWait = 精准打击(推荐)
⚠️ 轮询的本质:WebDriver 协议是 HTTP/JSON-Wire 协议,浏览器不能主动推事件给 Python,所以 Selenium 只能:每隔 N 毫秒问一次"条件满足了吗?" → 满足就返回,超时就报错。
Selenium 三种等待完整对比
分类 API 作用范围 轮询频率 等什么 评级
⏱ 硬等time.sleep(N)当前这一行无啥都不等,暂停线程 N 秒🔴 避免
🛡 隐式等待implicitly_wait(N)全局所有 find_element驱动内部(不固定)元素出现于 DOM🟡 谨慎
🎯 显式等待WebDriverWait().until()这一次调用可配(默认 500ms)任何条件(20+ 种)🟢 首选
关键认知:超时是"被动设上限,过了就放弃";等待是"主动轮询,等条件变好"。两者是共生关系:没有超时的等待会死等(脚本卡死),没有等待的超时时立即报错。
概念全景图 —— 一图看懂 Selenium 等待机制全貌
等待机制 Waits
硬等
time.sleep(N)
📍当前行 ⏱睡死 🔴无轮询
时间驱动
(不管条件)
隐式等待
implicitly_wait(N)
🌐全局 🔒固定条件 ⚡驱动内部
状态驱动 · 全局
(等元素出现)
显式等待
WebDriverWait
until()
条件成真
EC
预定义
lambda
自定义
until_not()
条件成假
EC
预定义
lambda
自定义
状态驱动 · 局部
(等条件变好)
time.sleep → 时间驱动
implicitly_wait → 状态 · 全局
WebDriverWait → 状态 · 局部
三大致命坑点 —— 90% 的自动化脚本至少踩过一个
坑 1:implicitly_wait 设太大(≥5s)
全局污染!所有 find_element 都被迫轮询 5 秒,连 "确认元素不存在" 这种本来瞬间返回的场景,也要傻等满 5 秒才抛 NoSuchElementException。
driver.implicitly_wait(10) # ❌ 设太大
try:
driver.find_element(By.ID, "ghost") # 不存在
except NoSuchElementException:
pass # 也要傻等 10 秒 💀
坑 2:隐式等待 + 显式等待叠加灾难
显式等待内部用 find_element,find_element 被隐式等待包了一层。最坏情况:隐式 10s × 显式 10 次轮询 = 几分钟!Selenium 自己都不知道怎么回事。
driver.implicitly_wait(10) # 隐式 10s
wait = WebDriverWait(driver, 15) # 显式 15s
wait.until(EC.presence_of_element_located((By.ID, "never"))) # 最坏 10s × 15 = 150s
坑 3:用 presence 代替 clickable 来点击按钮
presence 只保证 DOM 里有,不保证没被遮罩。点按钮遇到 ElementClickInterceptedException(被 loading 遮罩盖着),就是因为用了 presence / visibility 没检查是否可点击。
模拟等待的影响
about:blank
📋 Dashboard
Loading...
Loading...
Loading...
加载中...
加载中...
#
Name
Status
1
Alice
—
2
Bob
—
状态:空闲 ⏱ 0.00s
演示统计
选择下方按钮开始演示
观察等待机制的效果差异
「等待」设置
s
s
s (poll_frequency: s)
time.sleep
implicitly_wait
WebDriverWait
当前演示区配置:sleep=3s, implicit=5s, explicit=10s
选择等待方式模拟效果
⚠️ 以下模拟 等待超时、元素找不到 的场景:
演示重点: time.sleep 傻等不管页面 → 推荐用 WebDriverWait 精准等条件。
💡 元素等待策略建议
✅ 隐式等待要么全程不设(默认就是 0,最严谨),要么全程 ≤1s做极短兜底
❌ 避免设 ≥5s,更不要中途频繁切换隐式值(容易忘开回去、叠加变长)
✅ 所有关键步骤 用显式等待(WebDriverWait + EC)精准覆盖
❌ 避免用 time.sleep 代替 —— 机器快白等、慢就崩,CI/CD 上尤其不稳定
❌ 例外:等 CSS 动画完成 / 截图前稳定 / 调试时插入 time.sleep() 查看中间状态
✅ 显式等待条件匹配操作意图,条件层级由强到弱:clickable > visibility > presence
✅ 要点击某个按钮(操作意图:点击) → clickable(确保没被遮罩、没 disabled)
✅ 等提示消息出现(操作意图:等可见) → visibility(确保可见)
✅ 只关心 DOM 里有没有(操作意图:检查存在) → presence(确保元素在 DOM 中存在,可以是隐藏的)
❌ 别用弱等级条件代替强等级(例:别用 presence 代替 clickable 等待要点击的按钮 —— 元素可能在 DOM 里但被遮罩盖着或 disabled)
演示页面 —— 代码中的 4 种等待条件都在这里
加载中...
📊 用户控制台
📖 演示说明 (点击「模拟加载」再点击「登录」可观察完整流程)
点击「模拟加载」→ loader 旋转 2.5s 后消失,标题淡入、登录按钮解锁 → 点击「登录」→ URL 跳转 + 绿色 toast 出现。
代码 ↔ 元素 & EC 条件
EC.invisibility → #loader ← 遮罩消失
EC.visibility → #title ← 标题出现
EC.clickable → #login-btn ← 按钮可点
EC.url_contains → dashboard ← URL 跳转
EC.visibility → #toast-success ← 成功提示
💡 核心教学点
代码中 没有 time.sleep(),全靠 wait.until(EC.xxx)。
sleep 是硬等(网络快了浪费、慢了不够用),EC 是条件驱动(满足即往下走,自适应)。
最佳实践:三层等待(生产代码推荐模板)
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.common.exceptions import TimeoutException, NoSuchElementException

# ═══════════════════════════════════════
# 初始化:一层隐式等待兜底(生产代码不用 sleep)
# ═══════════════════════════════════════
driver = webdriver.Chrome()
driver.implicitly_wait(1) # 隐式等待:find_element 兜底 1s(极短兜底)

# 创建显式等待对象(单次精准打击)
wait = WebDriverWait(driver, 15, poll_frequency=0.3, ignored_exceptions=(NoSuchElementException,))

# ═══════════════════════════════════════
# 执行:显式等待链覆盖每个关键步骤
# ═══════════════════════════════════════
try:
    driver.get("https://example.com")

    wait.until(EC.invisibility_of_element_located((By.ID, "loader")))
    wait.until(EC.visibility_of_element_located((By.ID, "title")))

    login = wait.until(EC.element_to_be_clickable((By.ID, "login-btn")))
    login.click()

    wait.until(EC.url_contains("dashboard"))

    success = wait.until(EC.visibility_of_element_located((By.ID, "toast-success")))
    print(f"✅ 登录成功:{success.text}")
except TimeoutException as e:
    driver.save_screenshot("fail.png")
    print(f"❌ 超时:{e}")
finally:
    driver.quit()
Python Selenium · 超时设置与显式等待完整示例
# ① 启动浏览器
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

driver = webdriver.Chrome()

# ② 三类超时设置(单位:秒)
driver.set_page_load_timeout(30)     # 页面导航加载最多等 30s
driver.set_script_timeout(10)        # execute_script 最多等 10s
driver.implicitly_wait(1)            # find_element 兜底轮询最多 1s

driver.get("https://autotest-505.pages.dev/browser_op_tech_sections/timeout_wait_tech.html")

# ③ JS 注入:execute_script 受 set_script_timeout(10) 保护,脚本卡住最多抛超时异常
ready_state = driver.execute_script("return document.readyState")
print("页面加载状态:", ready_state)   # complete

# 注入 JS 直接读取元素文本(不经过 Selenium 定位器)
card_text = driver.execute_script("return document.getElementById('mockCard1').innerText;")
print("卡片内容:", card_text)

# ④ 普通定位:find_element 受 implicitly_wait(1) 保护,元素没立即出现时兜底轮询最多 1s
sleep_btn = driver.find_element(By.ID, "waitBtnSleep")
print("定位到按钮:", sleep_btn.text)

# ⑤ 显式等待(推荐):每 0.5s 轮询一次,最长等 10s
wait = WebDriverWait(driver, 10, poll_frequency=0.5)

# 等按钮可点击后再点击(id 与本演示页“显式等待”按钮一致)
btn = wait.until(EC.element_to_be_clickable((By.ID, "waitBtnExp")))
btn.click()

# 等异步加载的卡片变为可见(id 与本演示页动态元素一致)
wait.until(EC.visibility_of_element_located((By.ID, "mockCard1")))

# ⑥ 结束会话
driver.quit()
操作效果展示区域
点击上方按钮,这里会显示 Selenium 超时 API 的配置结果和关键数据

关于我们

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. 如涉及版权问题或内容建议,请及时通过联系方式与我们沟通,我们将第一时间处理。

CI/CD:自动化测试的真实运行环境
CI/CD 是什么?

● CI = Continuous Integration(持续集成)
代码推送到 Git 仓库后,自动拉代码 → 装依赖 → 跑测试,每次 push 都帮你检查有没有问题。

● CD = Continuous Delivery(持续交付)
CI 通过后自动打包 → 发到服务器 → 重启,不用手动部署。
(另一种 Continuous Deployment 是全自动部署到生产,不用人审批)

CI/CD 完整流水线

你写了一行代码 → git push origin main
↓
GitHub(或 GitLab / Jenkins / Gitee)收到推送
↓
自动触发一条 Pipeline(流水线)
① Checkout(拉代码)
把你刚推的代码拉到一台全新的云服务器上
↓
② Install(装依赖)
pip install -r requirements.txt
装 Chrome、装 ChromeDriver
↓
③ Build(构建)
打包成可部署的产物(.tar.gz / Docker 镜像 / ...)
↓
④ Test(跑测试)
pytest unit_tests/  # 单元测试
pytest selenium_tests/ # ⬅️ Selenium 在这里上场!
代码覆盖率检查 · 代码风格检查
↓
全部通过?
╱    ╲
是    否
↓    ↓
⑤ Deploy
自动发到服务器
生产环境立刻更新
❌ 失败
发邮件/钉钉通知你
修复 → 重新 push

Selenium 在 CI/CD 里扮演什么角色?
Selenium 测试脚本是流水线 第 ④ 阶段(Test) 的核心。CI 服务器会:
1. 在全新的云服务器上装 Python + Chrome + ChromeDriver
2. 以 headless(无头)模式启动 Chrome(没有 GUI,更快但渲染行为和本地略有差异)
3. 运行你的 pytest + Selenium 脚本,模拟真实用户操作(登录、搜索、下单...)
4. 全部通过才允许部署到生产;任何一步失败 → 发邮件通知 → 生产不会被污染

核心好处:每次代码合进来都有 Selenium 帮你模拟真实用户过一遍关键路径,而不是等到用户反馈"登录不了了"。

为什么 CI/CD 上 sleep 特别容易崩?
CI 环境每次都是全新的云服务器,和本地有几个关键差异:
因素 本地 CI/CD 云服务器
网络专线稳定数据中心共享,偶发抖动
CPU独占多租户,可能被抢占
浏览器缓存有没有(每次全新启动)
页面加载速度有缓存,秒出全量下载,慢 2~5 倍
并发任务只跑你这一份可能几十上百个同时抢资源

同样的 sleep(3):
本地:按钮 2s 出来 → sleep 3s → 白等 1s 但能过 ✅
CI 上:按钮 6s 出来 → sleep 3s 已经过了 → find_element 找不着 → 💥

这就是为什么说 CI/CD 上尤其不稳定 —— CI 是一台你控制不了但天天要给你打分的云电脑,每次环境都不一样。
而 WebDriverWait 是"等条件满足就行,最多等 10s 上限" —— 对 CI 环境的波动天然免疫。
为什么推荐 ≤1s
📌 隐式等待超时值的语义(W3C 规范定义)
隐式等待的"兜底窗口"即 implicitly_wait(N) 所设置的 N 值本身,此结论直接来自 W3C WebDriver 规范:
"A session has an associated session implicit wait timeout that specifies a time to wait for the implicit element location strategy"
→ 驱动在一次 findElement / findElements 调用中,最多愿意等待的总时间即为该值,不得超时。
implicit=0.5s → 驱动最多等待 500ms 后必须放弃
implicit=1.0s → 驱动最多等待 1000ms 后必须放弃
implicit=0 → 驱动立即执行一次查询,不进行重试(默认值)

以上定义为 W3C 规范层面的硬约束,与驱动实现无关,是选择 implicit 值、计算叠加成本、规划白等上限的直接依据。
🔍 驱动内部轮询机制——规范未作规定
W3C 规范对隐式等待仅定义了超时值的语义(见上文),对驱动内部的轮询实现细节完全未做约束。具体而言,规范未规定: 以 ChromeDriver 为例,可通过让元素延迟插入 DOM、记录 find_element 实际耗时的方法进行观测:在常规负载下元素出现后约 50~70ms 内即可被查到;若同一驱动连续处理多次请求,该值可能飘至 200ms 以上。换驱动版本或换机器,结果也可能不同。
🔧 实测代码(可自行跑一遍验证你的驱动) 原理:让元素在指定延迟后才插入 DOM,调用 find_element 记录实际耗时。耗时减去延迟得到"元素出现后驱动最晚多久查到"。
延迟点密集覆盖 10~1500ms,跑一轮后取差值区间的上界,即为该驱动在当前负载下的轮询间隔量级。
同一驱动连续处理多次查找请求,若差值区间明显漂移,说明驱动的轮询节奏已不稳定。
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

def test_polling(d, delay):
    d.implicitly_wait(2)
    d.get('about:blank')
    d.execute_script(f"setTimeout(()=>{{document.body.innerHTML='
t
'}},{delay})") t0 = time.perf_counter() try: cost = (time.perf_counter() - t0) * 1000 print(f"delay={delay:4d} 耗时={cost:6.0f} 差值={cost-delay:+5.0f}") except: print(f"delay={delay} 超时") driver = webdriver.Chrome() for delay in range(10, 1550, 50): # 50ms 步进,覆盖 10~1500ms test_polling(driver, delay) driver.quit()

⚖️ 那 ≤1s 怎么选出来的?—— 经验 + 权衡
经验观察
竞态(race condition,脚本发起 find_element 的时刻与元素实际出现于 DOM 的时刻之间的竞态)通常差 200~500ms: 注:以上是前端性能的普遍经验数字,不是数学公式,极端场景可能超出范围。

权衡一:为什么选 1s 而不是 0.5s?
500~1000ms 是 0.5s 和 1.0s 的兜底能力分水岭:
低于 500ms 两者都能兜;高于 1000ms 两者都兜不住(该用显式等待);
只有在 500~1000ms 之间,1.0s 比 0.5s 多出了实际价值: 代价对比(和显式等待 explicit=15s 混用时):
0.5s 叠加成本 = 0.5 × 30 = 15 秒 
1.0s 叠加成本 = 1.0 × 30 = 30 秒(多 15 秒)

→ 多花 15 秒叠加成本,换 500~1000ms 档竞态(race condition)的兜底覆盖。这是一个合理的折中,不是数学必然。
权衡二:为什么不设 2s?
2s 的兜底窗口确实更大,但代价全部翻倍,而兜底提升边际效益递减:

① 叠加变长成本:和显式等待混用时的最坏耗时公式:
最坏 = implicit × (explicit超时 / pollFreq)
注:这里的 pollFreq 是 显式等待 WebDriverWait 的 poll_frequency(Selenium Python 客户端默认 500ms,硬编码在 wait.py 里),不是隐式等待的轮询间隔。这个值是确定的。

假设 explicit=15s、pollFreq=0.5s:
implicit=1s → 1 × 30 = 30 秒 ✅ 可接受
implicit=2s → 2 × 30 = 60 秒 ⚠️ 翻倍(多 30 秒,兜底窗口只多 1000ms)
implicit=5s → 5 × 30 = 150 秒 💀 爆炸

② "确认元素不存在"的白等:你写了 10 个 try: find_element ... except NoSuchElementException:
implicit=1s → 白等 10 秒
implicit=2s → 白等 20 秒(翻倍)
implicit=5s → 白等 50 秒(爆炸)

竞态(race condition)通常差 200~500ms,1s 的兜底窗口完全兜得住绝大多数。
从 1s 升到 2s,兜底窗口只多了 1000ms,但叠加成本和白等全部翻倍——边际效益递减太明显。
✅ 最终结论
值 兜底窗口
(规范定义)
叠加成本
explicit=15s
白等
10次不存在
依据类型 推荐
不设(=0)0ms0 秒0 秒规范最严谨
1s1000ms30 秒10 秒经验+权衡默认推荐
0.5s500ms15 秒5 秒经验+权衡确认页面 <500ms
2s2000ms60 秒20 秒代价太大谨慎
≥5s≥5000ms150 秒+50 秒+爆炸❌ 绝对避免