这篇记录一下我在自动化中处理“页面假死 / 超时加载 / 元素卡住不动”的一些经验。

自动化一旦跑起来,就像装了马达的机器人,一步错就可能全部断掉。有时候是页面加载太慢,有时候是点击完页面不跳转,还有时候干脆浏览器卡死……不能指望一切顺利,还是得做点准备。


一、几种常见“假死”场景

以下这些问题你可能都遇到过:

  • 页面加载后,关键元素一直不出现(JS 太慢)
  • 点击按钮后页面长时间不跳转,也没有任何报错
  • 页面 DOM 不变,但请求其实在后台挂了(你以为还在 loading,其实早挂了)
  • 浏览器窗口卡住,连关闭都慢半拍
  • 脚本一直卡在某个 find_elementclick 上,不抛错也不动弹

二、基础防御措施:超时设置

1. 全局页面加载超时设置

driver.set_page_load_timeout(15)  # 超过15秒加载不出来就抛异常

适用于 .get() 加载慢的场景,可以避免永远卡住。


2. 显式等待比隐式等待靠谱

推荐用 WebDriverWait + 条件判断,控制流程等待。

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, timeout=10)
element = wait.until(EC.presence_of_element_located((By.ID, "main-content")))

你可以更换判断条件,比如:

  • element_to_be_clickable
  • visibility_of_element_located
  • title_contains 等等

三、封装级容错机制

建议把有可能“卡死”的操作包一层 retry:

import time

def safe_click(driver, by, value, max_retry=3):
    for i in range(max_retry):
        try:
            element = WebDriverWait(driver, 5).until(
                EC.element_to_be_clickable((by, value))
            )
            element.click()
            return
        except Exception as e:
            print(f"[尝试点击失败] 第{i+1}次: {e}")
            time.sleep(2)
    raise RuntimeError(f"多次点击失败,元素无法操作:{value}")

你可以套在任何“容易卡”的地方,比如点击按钮、填写表单、页面跳转。


四、更强一点:页面卡死检测 + 自动刷新

可以用一个“死循环保护器”:

from selenium.common.exceptions import TimeoutException

try:
    driver.set_page_load_timeout(10)
    driver.get("https://example.com")
except TimeoutException:
    print("页面加载超时,尝试刷新")
    driver.refresh()

这在处理网络波动或弱网环境时特别有效。


五、如何判断页面“根本没变”

有时候你点击了“下一步”,但页面内容完全没变化。可以通过 DOM 特征或 URL 判断:

before_url = driver.current_url
click_next()
time.sleep(2)

if driver.current_url == before_url:
    print("页面没跳转,可能卡住了")

也可以对某个关键元素的 text、数量进行对比,看是否刷新了页面内容。


六、最后的兜底:异步线程监控 + 截图退出

这个不常用,但如果你在跑大批量流程,建议加个守护线程定时检查状态、超时截图退出:

import threading

def watchdog(driver, interval=60):
    while True:
        time.sleep(interval)
        print("守护线程检查中…")
        driver.save_screenshot(f"watchdog_{int(time.time())}.png")

threading.Thread(target=watchdog, args=(driver,), daemon=True).start()

七、总结

Selenium 是单线程、强同步、极易阻塞的工具。你一旦在某一步没考虑异常或超时,整个脚本就挂住了。

我的建议是:

  • 所有 .get().click().find_element() 都做异常封装
  • 引入合理的 retry + watchdog
  • 不指望一切顺利运行,做好“失败但不崩”的容错策略

添加微信