在 Python 中使用 requests 库实现延迟(即请求之间的时间间隔)非常简单,主要有以下几种方法,适用于不同的场景。

使用 time.sleep() (最常用、最简单)
这是最直接的方法,即在两次请求之间调用 time.sleep() 函数,让程序暂停指定的秒数。
适用场景:当你需要精确控制每次请求之间的间隔时间时,例如爬取网站时避免请求过快。
示例代码:
import requests
import time
# 目标 URL
url = 'https://httpbin.org/delay/1' # 这个 URL 会故意延迟1秒再响应
# 循环发送3次请求
for i in range(3):
print(f"正在发送第 {i+1} 次请求...")
try:
# 发送 GET 请求
response = requests.get(url)
# 检查请求是否成功
response.raise_for_status() # 如果状态码不是 200,则会抛出异常
print(f"第 {i+1} 次请求成功,状态码: {response.status_code}")
# print(response.json()) # 打印响应内容
except requests.exceptions.RequestException as e:
print(f"第 {i+1} 次请求失败: {e}")
# 在两次请求之间暂停 2 秒
# 如果是最后一次请求,则不需要 sleep
if i < 2:
print(f"等待 2 秒...")
time.sleep(2) # 暂停 2 秒
print("所有请求已完成。")
优点:
- 简单直观,易于理解和使用。
- 可以精确控制每次请求之间的间隔。
缺点:
- 是同步的,
time.sleep()会阻塞整个程序的执行,在等待期间无法做其他事情。
使用 requests.Session() + time.sleep() (更高效)
如果你需要对同一个域名进行多次请求,使用 Session 对象是更好的选择。Session 对象会保持一个连接(TCP连接池),可以复用连接,提高效率,你仍然可以在 Session 的请求循环中使用 time.sleep()。
适用场景:需要对同一网站的多个 URL 进行多次请求,既要延迟,又要高效。
示例代码:
import requests
import time
# 创建一个 Session 对象
session = requests.Session()
# 目标 URL 列表
urls = [
'https://httpbin.org/get',
'https://httpbin.org/user-agent',
'https://httpbin.org/headers'
]
for i, url in enumerate(urls):
print(f"正在使用 Session 发送第 {i+1} 次请求到 {url}...")
try:
# 使用 Session 发送请求
response = session.get(url)
response.raise_for_status()
print(f"第 {i+1} 次请求成功,状态码: {response.status_code}")
except requests.exceptions.RequestException as e:
print(f"第 {i+1} 次请求失败: {e}")
# 在请求之间暂停 1.5 秒
if i < len(urls) - 1:
print(f"等待 1.5 秒...")
time.sleep(1.5)
# 关闭 Session,释放连接
session.close()
print("所有请求已完成。")
优点:
- 结合了
Session的高效(连接复用)和time.sleep()的简单可控性。 - 适合需要爬取同一网站多个页面的场景。
使用 requests.adapters 和 HTTPAdapter (高级用法)
requests 底层使用 urllib3 库,你可以通过配置 urllib3 的 HTTPAdapter 来设置全局的延迟,pool_connections 和 pool_blocksize,但这更像是控制连接池的参数,而不是请求之间的时间间隔。
注意:这种方法不直接提供“请求之间等待 N 秒”的功能,而是通过控制连接池的获取超时时间来间接影响请求速率,它更适合管理连接资源。
示例代码:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# 创建一个 Session 对象
session = requests.Session()
# 定义重试策略
retry_strategy = Retry(
total=3, # 总共重试3次
backoff_factor=1, # 指数退避因子,第一次重试等待 1 * 2^0 秒,第二次 1 * 2^1 秒...
status_forcelist=[500, 502, 503, 504] # 遇到这些状态码时重试
)
# 创建一个 HTTPAdapter 并应用重试策略
adapter = HTTPAdapter(max_retries=retry_strategy)
# 将 adapter 挂载到 Session 的 http 和 https 上
session.mount("http://", adapter)
session.mount("https://", adapter)
# --- 现在你仍然需要用 time.sleep 来控制请求间隔 ---
url = 'https://httpbin.org/get'
for i in range(3):
print(f"正在发送第 {i+1} 次请求...")
try:
response = session.get(url)
response.raise_for_status()
print(f"第 {i+1} 次请求成功")
except requests.exceptions.RequestException as e:
print(f"第 {i+1} 次请求失败: {e}")
if i < 2:
time.sleep(3) # 仍然需要手动添加延迟
session.close()
优点:
- 非常强大,可以处理网络不稳定、服务器临时错误等情况(通过重试机制)。
- 可以自定义连接池大小等底层参数。
缺点:
- 配置相对复杂。
- 不能替代
time.sleep()来实现请求间隔,它主要用于连接管理和错误恢复。
使用 concurrent.futures (异步并发)
如果你不想因为 time.sleep() 而阻塞整个程序,可以使用 concurrent.futures 来异步地发送请求,并通过 as_completed 结合 time.sleep 来管理整体速率。
适用场景:需要高效地发送多个请求,但又要控制整体的请求速率(每秒最多 N 个请求)。
示例代码:
import requests
import time
import concurrent.futures
def fetch_url(url, delay):
"""一个请求函数,包含延迟逻辑"""
print(f"准备请求 {url}...")
time.sleep(delay) # 在请求前等待
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
print(f"成功请求 {url}, 状态码: {response.status_code}")
return response.text
except requests.exceptions.RequestException as e:
print(f"请求 {url} 失败: {e}")
return None
urls = [
'https://httpbin.org/get',
'https://httpbin.org/delay/1',
'https://httpbin.org/delay/2',
'https://httpbin.org/delay/3'
]
# 设置总的请求延迟(每次请求间隔1秒)
request_delay = 1
# 使用 ThreadPoolExecutor 创建一个线程池
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
# 使用列表推导式提交任务,并为每个任务添加延迟
# 注意:这里的延迟是在任务提交前等待,而不是在任务执行期间
future_to_url = {
executor.submit(fetch_url, url, request_delay): url
for url in urls
}
# as_completed 会按照完成的顺序返回 future 对象
for future in concurrent.futures.as_completed(future_to_url):
url = future_to_url[future]
try:
data = future.result()
# 在这里处理返回的数据
# print(f"从 {url} 获取到的数据: {data[:100]}...")
except Exception as e:
print(f"处理 {url} 时发生错误: {e}")
print("所有异步请求已完成。")
注意:在这个异步例子中,time.sleep(delay) 是在任务提交到线程池之前执行的,这会使得主线程在提交任务时产生延迟,从而间接控制了任务提交的速率,如果想在任务内部执行时延迟逻辑,则需要将 time.sleep 放在 fetch_url 函数内部。
总结与最佳实践
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
time.sleep() |
简单、直接、精确 | 同步阻塞 | 简单脚本,需要精确控制间隔 |
Session + sleep |
高效+可控 | 仍是同步阻塞 | 对同一网站多次请求,需要延迟 |
HTTPAdapter |
强大,处理重试和连接池 | 配置复杂,不直接控制间隔 | 需要高可用性、稳定性的爬虫 |
concurrent.futures |
异步并发,高效 | 代码稍复杂,需要理解异步模型 | 需要高并发且控制整体请求速率 |
对于绝大多数情况,尤其是初学者和日常脚本,方法一 (time.sleep()) 和 方法二 (Session + time.sleep()) 是最常用和推荐的选择。
重要提醒:
在进行网络请求(尤其是爬虫)时,添加延迟 (time.sleep) 是一种礼貌且必要的行为,它可以:
- 避免对目标服务器造成过大压力。
- 降低被 IP 封禁的风险。
- 防止触发网站的反爬虫机制。
请根据你的具体需求选择最合适的方法。
