页面加载与元素等待超时设置
sleep(最差)→ implicit(全局隐式)→ explicit(推荐显式)
time.sleep() · implicitly_wait() · WebDriverWait().until() · expected_conditions三种等待方式对比:强制 sleep(最烂)→ 隐式 implicitly_wait(全局)→ 显式 WebDriverWait+EC(最专业)。
sleep 硬等 2 秒 → implicitly_wait 最多等 10 秒、元素一出现就返回 → WebDriverWait+EC 条件满足立即返回,元素 3.5 秒出现就只等 3.5 秒,绝不浪费。
# ===== 三种等待方式对比 =====
# ❌ 方式 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) # 隐式等待
| 等待方式 | 设定 t 秒 | 实际耗时 | 效率 | 你的备注 |
|---|---|---|---|---|
| 👆 请点击上方 demo-card 的 sleep / implicit / explicit 按钮,结果将自动填入 | ||||
# 👇 在下方写出你的 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("总共轮询了多少次?")
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>
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>
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)
pip install flask(只需装一次)cd 到 slow_server.py 所在目录,运行 python slow_server.py,看到 Running on http://127.0.0.1:5000 即启动成功。
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()
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()
time.sleep(t) / implicitly_wait(t) / WebDriverWait(driver,t).until(...) 去等一个按钮出现,用 time.perf_counter() 精确统计各自花了多少秒,对比三者差异wait_until(callable, timeout=10, poll=0.5) 函数:每 poll 秒轮询 callable() 返回值,为真立即返回;超时抛 TimeoutException;打印总等待了多少次driver.set_page_load_timeout(3) / driver.set_script_timeout(2) / driver.implicitly_wait(5),分别观察超时抛的是什么异常(TimeoutException / ScriptTimeoutException)implicitly_wait(10),再写一个 WebDriverWait(driver, 3).until(...) —— 如果元素不存在,显式等待实际会等 3 秒还是 10 秒?验证你的猜想下面会实时记录你在 9 个章节里的每一次操作。