杰瑞科技汇

python requests 超时

超时是网络编程中一个至关重要的概念,它能防止你的程序因为网络问题、服务器无响应或目标地址不可达而无限期地等待下去,导致程序“卡死”。

python requests 超时-图1
(图片来源网络,侵删)

为什么必须设置超时?

想象一下以下几种场景:

  1. 服务器宕机或网络中断:你请求的服务器已经下线,但你的代码不知道,它会一直等待,直到 TCP 连接超时(这可能需要很长时间)。
  2. 服务器处理缓慢:服务器接收到了请求,但由于内部逻辑复杂(如复杂的数据库查询、大量计算),处理时间非常长,迟迟不返回响应。
  3. 目标地址错误或防火墙阻拦:你请求的域名或 IP 地址错误,或者被中间的防火墙墙掉了,导致请求无法到达服务器。

在这些情况下,如果没有设置超时,你的 Python 脚本就会永远阻塞在 requests.get() 这一行,无法继续执行后续代码,也无法处理其他请求或任务。


requests 如何设置超时?

requests 库的请求方法(如 get, post, put, delete 等)都接受一个 timeout 参数,这个参数是控制“等待”行为的关键。

timeout 参数可以有两种形式:

python requests 超时-图2
(图片来源网络,侵删)

单个浮点数(简单超时)

当你只传入一个单一的数字(如 010)时,它表示的是 “连接超时 + 读取超时”的总和

  • 连接超时:指的是客户端与服务器建立 TCP 连接所等待的最长时间,如果在这个时间内连接不上,就会抛出 requests.exceptions.ConnectTimeout 异常。
  • 读取超时:指的是客户端在发送请求后,等待服务器返回响应数据的最长时间,如果在这个时间内没有收到任何数据,就会抛出 requests.exceptions.ReadTimeout 异常。

示例:

import requests
import time
try:
    # 设置总超时时间为 5 秒
    # 如果连接 + 读取的总时间超过 5 秒,就会抛出 Timeout 异常
    print("开始请求,设置总超时 5 秒...")
    response = requests.get('https://httpbin.org/delay/10', timeout=5)
    print("请求成功!")
    print(response.status_code)
except requests.exceptions.Timeout:
    print("请求超时了!")
except requests.exceptions.RequestException as e:
    print(f"发生其他请求错误: {e}")

分析: 上面的代码请求了一个会延迟 10 秒才响应的地址 (https://httpbin.org/delay/10),但我们设置了 timeout=5,5 秒后 requests 会放弃等待,抛出 Timeout 异常。

元组(精细超时控制)

为了更精确地控制超时行为,timeout 参数也可以接受一个元组,形式为 (connect_timeout, read_timeout)

  • connect_timeout:连接超时时间。
  • read_timeout:读取超时时间。

这种方式更灵活,可以根据实际需求分别设置,你希望快速发现无法连接的服务器,但可以容忍服务器在建立连接后稍慢地返回数据。

示例:

import requests
try:
    # 连接超时 3 秒,读取超时 10 秒
    print("开始请求,连接超时 3 秒,读取超时 10 秒...")
    response = requests.get('https://httpbin.org/delay/2', timeout=(3, 10))
    print("请求成功!")
    print(response.status_code)
except requests.exceptions.Timeout:
    print("请求超时了!")
except requests.exceptions.RequestException as e:
    print(f"发生其他请求错误: {e}")

分析: 这个请求会延迟 2 秒返回,我们的设置是连接 3 秒,读取 10 秒,总时间 2 < 3 + 102 < 10,所以请求会成功。

再看一个失败的例子:

import requests
try:
    # 连接超时 3 秒,读取超时 5 秒
    # 服务器会延迟 10 秒返回,这超过了读取超时时间
    print("开始请求,连接超时 3 秒,读取超时 5 秒...")
    response = requests.get('https://httpbin.org/delay/10', timeout=(3, 5))
    print("请求成功!")
    print(response.status_code)
except requests.exceptions.ReadTimeout:
    # 注意:这里捕获的是更具体的 ReadTimeout
    print("读取超时了!服务器响应太慢。")
except requests.exceptions.Timeout:
    print("请求超时了!")
except requests.exceptions.RequestException as e:
    print(f"发生其他请求错误: {e}")

分析: 连接成功建立(假设很快),但服务器需要 10 秒才返回数据,而我们设置的 read_timeout 只有 5 秒,5 秒后客户端会放弃等待,抛出 requests.exceptions.ReadTimeout 异常。


常见的超时异常

了解可能抛出的异常类型,有助于你编写更健壮的错误处理代码。

  • requests.exceptions.Timeout:这是一个通用的超时异常,当你使用单个数字作为 timeout 时,如果连接或读取任一阶段超时,都会抛出这个异常。
  • requests.exceptions.ConnectTimeout:连接超时,专门用于在建立连接阶段超时。
  • requests.exceptions.ReadTimeout:读取超时,专门用于在等待服务器返回数据阶段超时。
  • requests.exceptions.TooManyRedirects:重定向过多,当你设置了 allow_redirects=True (默认) 并且重定向次数超过了 requests 的上限时,会抛出此异常,这也可以看作是一种“超时”。
  • requests.exceptions.RequestException:这是所有 requests 异常的基类,捕获它可以捕获所有由 requests 引发的错误。

最佳实践和推荐

  1. 永远不要不设置超时:这是一个坏习惯,除非你有特殊需求,否则请始终为你的请求设置一个合理的 timeout

  2. 为不同场景设置不同的超时

    • 关键业务请求:可以设置较短的超时(如 timeout=(3, 5)),快速失败,然后重试或通知用户。
    • 后台任务/非关键请求:可以设置较长的超时(如 timeout=(10, 30)),给服务器更多处理时间,但也要避免无限等待。
  3. 使用 try...except 进行错误处理: 妥善处理超时和其他网络异常,让你的程序在遇到问题时能够优雅地降级或重试,而不是直接崩溃。

  4. 考虑重试机制: 对于瞬时性的网络抖动,一次超时不代表永久失败,可以实现一个简单的重试逻辑。

    示例:带重试机制的请求

    import requests
    from requests.exceptions import Timeout
    MAX_RETRIES = 3
    TIMEOUT = 5  # 总超时时间
    def fetch_url_with_retry(url):
        for attempt in range(MAX_RETRIES):
            try:
                print(f"尝试第 {attempt + 1} 次请求...")
                response = requests.get(url, timeout=TIMEOUT)
                response.raise_for_status()  # 如果状态码不是 200,则抛出 HTTPError
                return response
            except Timeout:
                print(f"第 {attempt + 1} 次请求超时。")
                if attempt == MAX_RETRIES - 1:
                    print(f"已达到最大重试次数 {MAX_RETRIES},请求最终失败。")
                    return None
            except requests.exceptions.RequestException as e:
                print(f"第 {attempt + 1} 次请求发生错误: {e}")
                return None # 对于其他错误,可能不需要重试
    # 使用示例
    response = fetch_url_with_retry('https://httpbin.org/delay/10')
    if response:
        print("最终请求成功!")
        print(response.json())
    else:
        print("最终请求失败。")
超时类型 描述 示例 可能抛出的异常
单个数字 连接和读取的总超时时间 requests.get(url, timeout=10) requests.exceptions.Timeout
元组 分别设置连接超时和读取超时 requests.get(url, timeout=(3, 10)) requests.exceptions.ConnectTimeout
requests.exceptions.ReadTimeout

设置超时是编写健壮网络应用的基本要求,根据你的应用场景,选择合适的超时策略,并做好异常处理,你的程序将会更加稳定可靠。

分享:
扫描分享到社交APP
上一篇
下一篇