杰瑞科技汇

Python requests 如何设置等待时间?

简单的固定时间等待 (time.sleep())

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

Python requests 如何设置等待时间?-图1
(图片来源网络,侵删)

适用场景:

  • 在循环中,为每个请求添加一个固定的延迟,以防止触发服务器的速率限制。
  • 模拟用户在不同页面之间的浏览行为。

示例代码:

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}")

优点:

  • 简单易懂,实现方便。

缺点:

Python requests 如何设置等待时间?-图2
(图片来源网络,侵删)
  • 不够智能,即使服务器响应很快,你依然会等待满指定的时间,降低了效率。
  • 如果服务器响应很慢,固定的时间可能不足以保证下一个请求的顺利发送。

智能的动态等待 (更推荐)

更高级的等待方式是根据服务器的响应来动态调整等待时间。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,而是 awaitawait 会暂停当前异步函数的执行,将控制权交回事件循环,从而允许其他任务(如其他网络请求)在等待期间执行。

示例代码 (需要先安装 aiohttpasyncio): 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 密集型任务。 效率极高,非阻塞。 代码结构更复杂,需要理解异步编程。

给你的建议:

  1. 对于初学者和大多数脚本始终使用 requests.Session() 并为每个请求设置 timeout,这是编写健壮、高效 requests 代码的基石。
  2. 如果需要模拟人类行为或担心被封:在 Session 的循环请求中,加入一个较小的 time.sleep()time.sleep(1)time.sleep(random.uniform(0.5, 1.5)),让请求间隔看起来更自然。
  3. 如果需要处理大量数据或高并发:学习并使用 aiohttp,它会带来数量级的性能提升。
分享:
扫描分享到社交APP
上一篇
下一篇