Python使用Selenium实现网页自动化之等待与高级操作

为什么需要等待?

现代网页大量使用 AJAX 技术,页面元素不是一次性加载完的:

  1. 先加载 HTML 骨架
  2. 再加载 CSS 样式
  3. 然后执行 JavaScript 动态渲染内容
  4. 最后可能还要异步请求数据(比如评论区、推荐内容)

如果你用 browser.get() 打开页面后立刻去抓元素,很可能抓不到——因为元素还没加载出来!

三种解决方案:

等待方式 特点 适用场景
强制等待 简单粗暴,睡几秒 调试时临时用
隐式等待 全局设置,智能等待 整个脚本统一配置
显式等待 精确控制,条件触发 特定元素需要等待

延时等待的三种方式

1. 强制等待(time.sleep)

最简单粗暴的方式:让程序睡几秒,不管页面有没有加载完。

import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service

s = Service(r'D:\driver\chromedriver.exe')
browser = webdriver.Chrome(service=s)

# 打开百度
browser.get(r'https://www.baidu.com')

# 强制等待5秒
# 不管页面有没有加载完,都等5秒
time.sleep(5)

# 这时候再去操作元素
browser.find_element(By.ID, 'kw').send_keys('python')

browser.quit()

优点: 简单,不用动脑 缺点:

  • 浪费时间(页面1秒就加载完了,还要傻等4秒)
  • 不靠谱(网络慢的时候5秒可能不够)
  • 效率低,不适合大规模爬虫

建议: 调试时临时用,正式代码尽量不用。


2. 隐式等待(implicitly_wait)

全局设置一个最大等待时间,Selenium 会在找元素时自动等待:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service

s = Service(r'D:\driver\chromedriver.exe')
browser = webdriver.Chrome(service=s)

# 隐式等待:设置最大等待时间10秒
# 找元素时,如果元素还没加载出来,会等一会儿再试
# 直到元素出现,或者超过10秒才报错
browser.implicitly_wait(10)

# 打开百度
browser.get(r'https://www.baidu.com')

# 这行代码会智能等待:
# - 如果搜索框已经加载出来了,立刻执行
# - 如果没加载出来,每隔一段时间检查一次
# - 最多等10秒,超时就报错
input_box = browser.find_element(By.ID, 'kw')
input_box.send_keys('python')

print(f"当前网址: {browser.current_url}")
print(f"页面标题: {browser.title}")

browser.quit()

工作原理:

找元素 → 元素出现了吗?
    ↓ 是
    立刻返回元素
    ↓ 否
    等一会儿(默认0.5秒)再试
    ↓ 超过最大等待时间
    抛出 NoSuchElementException 异常

优点:

  • 全局设置一次,所有找元素操作都生效
  • 智能等待,元素提前出现就不用傻等

缺点:

  • 只针对"找元素"操作,其他操作不等待
  • 不够灵活,不能针对不同元素设置不同等待时间

3. 显式等待(WebDriverWait)

最灵活、最强大的等待方式。设置一个等待条件,每隔一段时间检查一次,条件满足就立刻继续:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.common.by import By
import time

s = Service(r'D:\driver\chromedriver.exe')
browser = webdriver.Chrome(service=s)

# 打开百度
browser.get(r'https://www.baidu.com')

# 创建显式等待对象
# 参数:浏览器驱动、最大等待时间(秒)
wait = WebDriverWait(browser, 10)

# 设置等待条件:等待 id='kw' 的元素加载出来
# presence_of_element_located:元素存在于 DOM 中即可(不一定可见)
input_box = wait.until(ec.presence_of_element_located((By.ID, 'kw')))

# 元素加载出来了,继续执行
input_box.send_keys('Python')
print("搜索框已找到并输入")

time.sleep(2)
browser.quit()

WebDriverWait 构造函数:

WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)
参数 说明 默认值
driver 浏览器驱动对象 必填
timeout 最大等待时间(秒) 必填
poll_frequency 检查间隔(秒) 0.5
ignored_exceptions 忽略哪些异常 None

两种等待方式:

# until:条件成立时继续执行
wait.until(条件)

# until_not:条件不成立时继续执行(相反逻辑)
wait.until_not(条件)

4. 常用等待条件(expected_conditions)

Selenium 提供了很多内置的等待条件,不用自己写:

from selenium.webdriver.support import expected_conditions as ec
条件 说明 示例
title_is('标题') 标题完全匹配 等待页面跳转完成
title_contains('关键字') 标题包含关键字 等待页面加载
presence_of_element_located((By.ID, 'kw')) 元素存在于 DOM 等待元素加载
visibility_of_element_located((By.ID, 'kw')) 元素可见 等待元素显示
invisibility_of_element_located((By.ID, 'loading')) 元素不可见 等待加载动画消失
element_to_be_clickable((By.ID, 'btn')) 元素可点击 等待按钮可用
text_to_be_present_in_element((By.ID, 'msg'), '成功') 元素包含指定文本 等待提示信息
alert_is_present() 弹窗出现 等待 alert 弹窗
frame_to_be_available_and_switch_to_it('iframe') iframe 可切换 等待 iframe 加载

实战示例:

from selenium.webdriver.support import expected_conditions as ec

# 等待标题包含"百度"
wait.until(ec.title_contains('百度'))

# 等待元素可见(不只是存在,还要显示出来)
wait.until(ec.visibility_of_element_located((By.ID, 'kw')))

# 等待按钮可点击
wait.until(ec.element_to_be_clickable((By.ID, 'su')))

# 等待加载动画消失
wait.until(ec.invisibility_of_element_located((By.CLASS_NAME, 'loading')))

三种等待方式对比:

对比项 强制等待 隐式等待 显式等待
设置方式 time.sleep(n) implicitly_wait(n) WebDriverWait()
作用范围 当前行 全局 特定元素
智能程度 傻等 较智能 最智能
灵活性 一般
推荐度 ⭐⭐⭐ ⭐⭐⭐⭐⭐

最佳实践:

  • 优先使用 显式等待
  • 可以配合 隐式等待 做全局兜底
  • 强制等待 只在调试时用

运行 JavaScript

Selenium 可以直接在页面中执行 JavaScript 代码,这是它的一个强大功能:

1. 滚动页面

很多网页是懒加载的,需要滚动到页面底部才会加载更多内容:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import time

s = Service(r'D:\driver\chromedriver.exe')
browser = webdriver.Chrome(service=s)

# 打开知乎发现页
browser.get('https://www.zhihu.com/explore')
time.sleep(2)

# 执行 JavaScript:滚动到页面底部
# document.body.scrollHeight 获取页面总高度
browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
print("已滚动到页面底部")

# 弹出提示框
browser.execute_script('alert("已滚动到页面底部")')

time.sleep(5)
browser.quit()

常用滚动操作:

# 滚动到页面底部
browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')

# 滚动到页面顶部
browser.execute_script('window.scrollTo(0, 0)')

# 向下滚动 500 像素
browser.execute_script('window.scrollBy(0, 500)')

# 滚动到指定元素位置
element = browser.find_element(By.ID, 'target')
browser.execute_script('arguments[0].scrollIntoView();', element)

2. 获取页面信息

# 获取页面标题(和 browser.title 一样)
title = browser.execute_script('return document.title;')

# 获取页面 URL(和 browser.current_url 一样)
url = browser.execute_script('return document.URL;')

# 获取页面所有 cookie
cookies = browser.execute_script('return document.cookie;')

3. 修改页面元素

# 修改元素属性
browser.execute_script(
    'arguments[0].setAttribute("value", "新值");', 
    element
)

# 隐藏元素
browser.execute_script(
    'arguments[0].style.display = "none";', 
    element
)

# 点击元素(某些元素用 Selenium 的 click() 点不了时)
browser.execute_script('arguments[0].click();', element)

什么时候需要用 execute_script?

  • 页面滚动(懒加载)
  • Selenium 原生方法搞不定的点击
  • 需要获取或修改页面内部状态
  • 绕过某些前端限制

Cookie 是网站存储在用户浏览器里的小文件,用来记录登录状态、用户偏好等。Selenium 可以方便地操作 Cookie。

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import time

s = Service(r'D:\driver\chromedriver.exe')
browser = webdriver.Chrome(service=s)

# 打开知乎
browser.get('https://www.zhihu.com/explore')

# 获取所有 Cookie
cookies = browser.get_cookies()
print(f'所有 Cookie:')
for cookie in cookies:
    print(f"  {cookie['name']}: {cookie['value'][:20]}...")

# 获取单个 Cookie
cookie = browser.get_cookie('name')

browser.quit()

Cookie 包含的字段:

字段 说明
name Cookie 名称
value Cookie 值
domain 所属域名
path 有效路径
expiry 过期时间(时间戳)
secure 是否只在 HTTPS 传输
httpOnly 是否禁止 JavaScript 访问

# 添加一个 Cookie
browser.add_cookie({
    'name': 'my_cookie',
    'value': 'my_value',
    'domain': '.zhihu.com',
    'path': '/'
})

# 添加后查看
print(f'添加后的 Cookie:{browser.get_cookies()}')

注意: 添加 Cookie 前必须先访问过该域名,否则会报错!


# 删除指定 Cookie
browser.delete_cookie('name')

# 删除所有 Cookie
browser.delete_all_cookies()
print(f'删除后 Cookie:{browser.get_cookies()}')  # 空列表 []

场景: 每次运行脚本都要重新登录,很麻烦。可以第一次手动登录后保存 Cookie,以后直接加载 Cookie 就能保持登录。

import json
from selenium import webdriver
from selenium.webdriver.chrome.service import Service

s = Service(r'D:\driver\chromedriver.exe')
browser = webdriver.Chrome(service=s)

# 第一步:手动登录,保存 Cookie
browser.get('https://www.zhihu.com')
print("请手动登录...")
input("登录完成后按回车继续...")

# 保存 Cookie 到文件
cookies = browser.get_cookies()
with open('cookies.json', 'w') as f:
    json.dump(cookies, f)
print("Cookie 已保存")

# 第二步:下次运行时加载 Cookie
browser.delete_all_cookies()
with open('cookies.json', 'r') as f:
    cookies = json.load(f)
    for cookie in cookies:
        browser.add_cookie(cookie)

# 刷新页面,此时应该是登录状态
browser.refresh()
print("已使用 Cookie 登录")

browser.quit()

Cookie 的妙用:

  • 保持登录 - 不用每次都扫码/输密码
  • 绕过验证 - 加载已验证的 Cookie
  • 多账号切换 - 保存多个账号的 Cookie
  • 爬虫伪装 - 带上正常用户的 Cookie,降低被封概率

实战案例:自动登录代理网站

案例背景

写爬虫时经常需要代理 IP,很多网站提供代理 IP 服务。我们以自动登录一个代理网站为例,展示完整的自动化流程。

完整代码

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
import time

s = Service(r'D:\driver\chromedriver.exe')
browser = webdriver.Chrome(service=s)

# 设置隐式等待
browser.implicitly_wait(10)

# 打开登录页面
browser.get('http://www.66daili.cn/UserManage/Login/')

# 输入账号
browser.find_element(By.ID, 'username').send_keys('youbafu')

# 输入密码
browser.find_element(By.ID, 'password').send_keys('youbafu123')

# 勾选"记住账号"
remember = browser.find_element(By.ID, 'remember')
# 判断是否已勾选,没勾选就点击
if not remember.get_attribute('checked'):
    remember.click()

# 点击登录按钮
browser.find_element(By.ID, 'submit').click()

# 等待登录完成
time.sleep(2)

print("登录成功!")

# 保持页面打开,方便查看
browser.quit()

代码解析:

  1. 隐式等待 - 设置10秒,找元素时自动等待
  2. 输入账号密码 - 找到输入框,send_keys 输入
  3. 勾选记住账号 - 先判断是否已勾选,避免重复点击
  4. 点击登录 - 提交表单
  5. 等待 - 给页面跳转留时间

文档总结

一、核心知识点回顾

1. 三种等待方式

等待方式 代码 原理 适用场景
强制等待 time.sleep(n) 程序暂停 n 秒 调试时临时用
隐式等待 implicitly_wait(n) 找元素时最多等 n 秒 全局统一配置
显式等待 WebDriverWait(driver, n) 条件满足立刻继续 特定元素精确控制

选择建议:

  • 优先用 显式等待(最灵活、最省时间)
  • 可以配合 隐式等待 做兜底
  • 强制等待 只在调试时用,正式代码避免

2. 显式等待的常用条件

from selenium.webdriver.support import expected_conditions as ec

# 元素相关
ec.presence_of_element_located((By.ID, 'kw'))      # 元素存在于 DOM
ec.visibility_of_element_located((By.ID, 'kw'))    # 元素可见
ec.element_to_be_clickable((By.ID, 'btn'))         # 元素可点击
ec.invisibility_of_element_located((By.ID, 'loading'))  # 元素消失

# 页面相关
ec.title_contains('百度')                           # 标题包含
ec.title_is('百度一下,你就知道')                    # 标题完全匹配

# 其他
ec.alert_is_present()                              # 弹窗出现
ec.text_to_be_present_in_element((By.ID, 'msg'), '成功')  # 文本出现

记忆口诀:

  • presence = 存在(在 DOM 里就行)
  • visibility = 可见(不仅要存在,还要显示出来)
  • clickable = 可点击(可见 + 启用)

3. 运行 JavaScript

# 滚动页面
browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')

# 滚动到元素
element = browser.find_element(By.ID, 'target')
browser.execute_script('arguments[0].scrollIntoView();', element)

# 点击元素(Selenium 点不了时用)
browser.execute_script('arguments[0].click();', element)

什么时候用?

  • 懒加载页面需要滚动
  • Selenium 原生方法失效
  • 需要获取/修改页面内部状态

# 获取
browser.get_cookies()          # 所有 Cookie
browser.get_cookie('name')     # 单个 Cookie

# 添加
browser.add_cookie({'name': 'xxx', 'value': 'yyy'})

# 删除
browser.delete_cookie('name')  # 删除指定
browser.delete_all_cookies()   # 删除所有

Cookie 的妙用:

  • 保持登录状态(不用每次都登录)
  • 多账号切换(保存多套 Cookie)
  • 爬虫伪装(带上正常用户的 Cookie)

二、常见坑与解决方案

坑1:显式等待和隐式等待混用导致超时异常

原因:两者时间叠加,可能超过预期
解决:优先只用显式等待,或注意时间设置

坑2:添加 Cookie 报错

原因:没先访问对应域名
解决:先 browser.get('网址'),再 add_cookie()

坑3:execute_script 点击无效

原因:元素还没加载出来
解决:先等待元素出现,再执行 JS

坑4:Cookie 保存后加载还是未登录

原因:Cookie 过期了,或缺少关键字段
解决:检查 Cookie 的 expiry 时间,确保完整保存

三、学习建议

  1. 等待是重中之重 —— 80% 的 Selenium 问题都是等待问题
  2. 显式等待优先 —— 比强制等待高效,比隐式等待灵活
  3. Cookie 是神器 —— 学会保存和加载 Cookie,省去大量登录时间
  4. JS 是兜底方案 —— Selenium 搞不定的,试试 execute_script
  5. 多写多调 —— 网页结构经常变,代码要跟着调整
posted @ 2026-08-02 13:29  渣男教父  阅读(20)  评论(0)    收藏  举报