简单的固定时间等待 (time.sleep())
这是最直接、最简单的等待方式,无论服务器是否响应,脚本都会暂停执行指定的时间,这在需要模拟人类操作、避免请求过于频繁时非常有用。

适用场景:
- 在循环中,为每个请求添加一个固定的延迟,以防止触发服务器的速率限制。
- 模拟用户在不同页面之间的浏览行为。
示例代码:
import requests
import time
# 目标 URL,这里以一个公共 API 为例
url = "https://api.github.com/users/octocat"
print("准备发送请求...")
# 在发送请求前等待 2 秒
time.sleep(2)
try:
response = requests.get(url)
response.raise_for_status() # 如果请求失败 (状态码 4xx 或 5xx),则抛出异常
print(f"请求成功!状态码: {response.status_code}")
print(f"用户名: {response.json()['name']}")
except requests.exceptions.RequestException as e:
print(f"请求发生错误: {e}")
# 在下一次请求前再等待 3 秒
print("等待 3 秒后发送下一个请求...")
time.sleep(3)
try:
response = requests.get(url)
response.raise_for_status()
print(f"第二次请求成功!状态码: {response.status_code}")
except requests.exceptions.RequestException as e:
print(f"第二次请求发生错误: {e}")
优点:
- 简单易懂,实现方便。
缺点:

- 不够智能,即使服务器响应很快,你依然会等待满指定的时间,降低了效率。
- 如果服务器响应很慢,固定的时间可能不足以保证下一个请求的顺利发送。
智能的动态等待 (更推荐)
更高级的等待方式是根据服务器的响应来动态调整等待时间。requests 库本身不直接提供这种功能,但我们可以通过以下两种常用方法实现:
检查响应状态码,根据状态码调整等待时间
这是最常用且最有效的方法之一,当你收到 429 Too Many Requests(请求过多)状态码时,就应该等待更长时间。
示例代码:
import requests
import time
url = "https://httpbin.org/status/429" # 这是一个专门返回 429 状态码的测试 URL
max_retries = 3
retry_delay = 5 # 初始等待 5 秒
for attempt in range(max_retries):
try:
print(f"尝试第 {attempt + 1} 次请求...")
response = requests.get(url)
if response.status_code == 200:
print("请求成功!")
break
elif response.status_code == 429:
# 如果是 429 错误,打印服务器建议的等待时间(如果有的话)
retry_after = response.headers.get('Retry-After', retry_delay)
print(f"请求过于频繁,需要等待 {retry_after} 秒后重试...")
time.sleep(int(retry_after))
else:
# 其他错误,直接重试
print(f"收到错误状态码: {response.status_code}, {retry_delay} 秒后重试...")
time.sleep(retry_delay)
except requests.exceptions.RequestException as e:
print(f"请求发生异常: {e}, {retry_delay} 秒后重试...")
time.sleep(retry_delay)
else:
# for 循环正常结束(即用尽了所有重试次数)
print("所有重试均失败,放弃请求。")
优点:
- 非常智能,能直接响应服务器的限制。
- 通过
Retry-After响应头,可以遵循服务器给出的明确等待时间,这是最规范的作法。
使用 requests.Session 对象保持连接并设置超时
Session 对象会为你保持一个 TCP 连接(称为“持久连接”或“连接池”),当你对同一个域名发起多次请求时,可以复用连接,避免了每次都进行三次握手,这本身就能极大地减少请求的“等待”时间(主要是网络延迟)。
为每个请求设置 timeout 参数至关重要。timeout 可以防止你的脚本因为网络问题或服务器无响应而无限期地卡住。
timeout 可以是一个单一的数字(代表连接和读取的总超时时间),或者一个元组 (connect_timeout, read_timeout)。
connect_timeout: 连接服务器超时时间。read_timeout: 从服务器读取数据超时时间。
示例代码:
import requests
# 创建一个 Session 对象
session = requests.Session()
# 目标 URL
url = "https://api.github.com/users/octocat"
try:
# 设置总超时时间为 10 秒
# 这意味着从连接建立到读取完数据的整个过程不能超过 10 秒
print("发送带 10 秒总超时的请求...")
response = session.get(url, timeout=10)
response.raise_for_status()
print(f"请求成功!状态码: {response.status_code}")
except requests.exceptions.Timeout:
print("请求超时!服务器在 10 秒内没有响应。")
except requests.exceptions.ConnectionError:
print("连接错误!无法连接到服务器。")
except requests.exceptions.RequestException as e:
print(f"请求发生其他错误: {e}")
# 下一次请求到同一个域名,Session 会复用连接
print("发送第二个请求(将复用连接)...")
try:
response = session.get(url, timeout=5) # 这次只等待 5 秒
response.raise_for_status()
print(f"第二次请求成功!状态码: {response.status_code}")
except requests.exceptions.RequestException as e:
print(f"第二次请求发生错误: {e}")
优点:
- 性能提升:通过连接池复用 TCP 连接,显著减少网络延迟。
- 可靠性提升:
timeout参数防止脚本无限制等待,使程序更加健壮。 - 状态保持:可以方便地处理 cookies 和 headers。
在异步请求中等待 (aiohttp)
如果你的应用场景需要高并发(例如爬取大量网页),使用 requests 的同步方式会非常慢,因为 requests.get() 是一个阻塞操作,这时,你应该使用异步 HTTP 客户端,如 aiohttp。
在异步编程中,等待不叫 sleep,而是 await。await 会暂停当前异步函数的执行,将控制权交回事件循环,从而允许其他任务(如其他网络请求)在等待期间执行。
示例代码 (需要先安装 aiohttp 和 asyncio): pip install aiohttp
import aiohttp
import asyncio
async def fetch_url(session, url):
try:
# async with 语句用于管理异步资源
# timeout 参数同样重要
async with session.get(url, timeout=10) as response:
# response.text() 也是异步操作,需要 await
html = await response.text()
print(f"成功获取 {url}, 状态码: {response.status}")
return html
except asyncio.TimeoutError:
print(f"请求 {url} 超时")
except Exception as e:
print(f"请求 {url} 时发生错误: {e}")
async def main():
urls = [
"https://api.github.com/users/octocat",
"https://httpbin.org/get",
"https://example.com"
]
# 创建一个 aiohttp ClientSession
async with aiohttp.ClientSession() as session:
# 使用 asyncio.gather 并发执行多个 fetch_url 任务
# 这比一个一个请求快得多
tasks = [fetch_url(session, url) for url in urls]
await asyncio.gather(*tasks)
# 运行主异步函数
asyncio.run(main())
优点:
- 高并发:可以在单个线程内同时处理成百上千个请求,效率极高。
- 非阻塞:在等待一个请求的响应时,可以处理其他请求,CPU 资源利用率高。
总结与最佳实践
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
time.sleep() |
简单脚本,需要固定延迟来模拟人类或避免频率限制。 | 实现简单。 | 不智能,效率低。 |
状态码检查 + Retry-After |
需要严格遵守服务器速率限制的健壮应用。 | 最智能、最规范,能自适应服务器。 | 逻辑稍复杂。 |
requests.Session + timeout |
强烈推荐,几乎所有需要多次请求的场景。 | 性能好(连接复用),可靠性高(防止卡死)。 | 是标准做法,无缺点。 |
aiohttp + await |
高并发爬虫、API 调用等 I/O 密集型任务。 | 效率极高,非阻塞。 | 代码结构更复杂,需要理解异步编程。 |
给你的建议:
- 对于初学者和大多数脚本:始终使用
requests.Session()并为每个请求设置timeout,这是编写健壮、高效requests代码的基石。 - 如果需要模拟人类行为或担心被封:在
Session的循环请求中,加入一个较小的time.sleep(),time.sleep(1)或time.sleep(random.uniform(0.5, 1.5)),让请求间隔看起来更自然。 - 如果需要处理大量数据或高并发:学习并使用
aiohttp,它会带来数量级的性能提升。
