【干货满满】如何处理requests库调用API接口时的异常情况

在使用requests库调用 API 接口时,异常情况(如网络波动、服务器错误、参数错误等)难以避免。若不妥善处理,可能导致程序崩溃或数据丢失。以下是系统化的异常处理方案,涵盖常见异常类型、处理策略、实战代码最佳实践,确保接口调用稳定可靠。

一、requests 库常见异常类型

requests库的异常主要分为两类:HTTP 错误状态码 (如 404、500)和网络 / 连接异常(如超时、连接失败)。具体分类如下:

异常类型 触发场景 示例
HTTP 错误 服务器返回错误状态码(4xx 客户端错误、5xx 服务端错误) 401(未授权)、404(接口不存在)、500(服务器内部错误)
连接异常 网络中断、域名解析失败、目标服务器不可达 无网络时调用接口、URL 域名错误(如https://xxx.invalid
超时异常 连接或读取数据超过设定时间未完成 接口响应过慢(如 10 秒未返回)、服务器负载过高
请求 URL / 参数异常 URL 格式错误、参数类型不匹配 URL 含特殊字符未编码、POST 请求体格式错误(非 JSON)
其他未知异常 不可预见的错误(如库本身 bug、系统资源不足) 内存溢出导致请求中断

二、异常处理的核心策略

针对不同异常类型,需设计差异化处理逻辑,核心原则是:明确错误原因→针对性恢复→避免程序崩溃

1. HTTP 错误:解析状态码,明确错误类型

服务器返回的状态码(response.status_code)是定位问题的关键,需根据状态码采取不同措施:

  • 4xx 客户端错误 (如 400、401、403、404):通常是调用方问题,需检查参数、权限、URL 是否正确,不建议重试(重试也会失败)。
  • 5xx 服务端错误 (如 500、502、503):通常是服务器临时故障,可重试(如间隔 1 秒后重试 2-3 次)。

2. 连接 / 超时异常:重试机制 + 资源释放

网络波动或服务器临时不可达时,重试是有效的恢复手段:

  • 重试间隔采用指数退避策略(如 1 秒→2 秒→4 秒),避免加重服务器负担。
  • 重试次数控制在 3 次以内,防止无限循环。

3. 参数 / 格式异常:提前校验,避免无效请求

调用接口前校验参数合法性(如必填字段是否存在、数据类型是否正确),减少因参数错误导致的失败。

三、实战代码:完整异常处理框架

以下是封装好的接口调用函数,集成了各类异常处理逻辑,可直接复用:

python 复制代码
import requests
import time
from requests.exceptions import (
    HTTPError,
    ConnectionError,
    Timeout,
    RequestException
)

def call_api_with_retry(
    url,
    method="GET",
    params=None,
    json=None,
    headers=None,
    timeout=10,
    max_retries=3,
    retry_delay=1
):
    """
    带异常处理和重试机制的API调用函数
    :param url: 接口URL
    :param method: 请求方法(GET/POST/PUT/DELETE)
    :param params: GET请求参数(字典)
    :param json: POST/PUT请求体(字典,自动转为JSON)
    :param headers: 请求头(字典)
    :param timeout: 超时时间(秒)
    :param max_retries: 最大重试次数(针对5xx和网络异常)
    :param retry_delay: 初始重试间隔(秒,指数退避)
    :return: 接口返回的JSON数据(成功)或None(失败)
    """
    # 初始化请求参数(默认值处理)
    params = params or {}
    json = json or {}
    headers = headers or {"Content-Type": "application/json"}
    retries = 0  # 当前重试次数

    while retries <= max_retries:
        try:
            # 发送请求
            if method.upper() == "GET":
                response = requests.get(
                    url,
                    params=params,
                    headers=headers,
                    timeout=timeout
                )
            elif method.upper() == "POST":
                response = requests.post(
                    url,
                    json=json,
                    headers=headers,
                    timeout=timeout
                )
            else:
                # 支持其他方法(PUT/DELETE等)
                response = requests.request(
                    method,
                    url,
                    json=json,
                    params=params,
                    headers=headers,
                    timeout=timeout
                )

            # 检查HTTP错误状态码(4xx/5xx)
            response.raise_for_status()  # 若状态码>=400,抛出HTTPError

            # 解析JSON响应(若接口返回非JSON,会抛出ValueError)
            return response.json()

        except HTTPError as e:
            # 处理HTTP错误(4xx/5xx)
            status_code = response.status_code
            if 400 <= status_code < 500:
                # 4xx客户端错误:不重试,记录错误后返回
                print(f"客户端错误:{status_code},URL:{url},详情:{str(e)}")
                return None
            elif 500 <= status_code < 600:
                # 5xx服务端错误:可重试
                print(f"服务端错误:{status_code},准备重试({retries}/{max_retries})")
            else:
                print(f"未知HTTP错误:{status_code},详情:{str(e)}")
                return None

        except ConnectionError as e:
            # 处理连接异常(网络问题、服务器不可达)
            print(f"连接失败:{str(e)},准备重试({retries}/{max_retries})")

        except Timeout as e:
            # 处理超时异常
            print(f"请求超时({timeout}秒):{str(e)},准备重试({retries}/{max_retries})")

        except ValueError as e:
            # 处理JSON解析失败(接口返回非JSON格式)
            print(f"响应解析失败(非JSON):{str(e)},URL:{url}")
            return None

        except RequestException as e:
            # 处理其他requests库异常(如URL格式错误)
            print(f"请求异常:{str(e)},URL:{url}")
            return None

        except Exception as e:
            # 处理所有未捕获的异常(兜底)
            print(f"未知错误:{str(e)},URL:{url}")
            return None

        # 重试逻辑(仅当未达到最大重试次数时)
        retries += 1
        if retries > max_retries:
            print(f"已达最大重试次数({max_retries}次),请求失败")
            return None
        # 指数退避:间隔 = 初始延迟 * (2^重试次数)
        delay = retry_delay * (2 **(retries - 1))
        print(f"重试间隔:{delay}秒...")
        time.sleep(delay)

    return None

四、使用示例与效果验证

1. 调用正常接口(成功案例)

python

运行

ini 复制代码
# 调用一个正常的API(如JSONPlaceholder测试接口)
result = call_api_with_retry(
    url="https://jsonplaceholder.typicode.com/posts/1",
    method="GET"
)
if result:
    print("接口返回:", result["title"])  # 输出:sunt aut facere repellat provident occaecati excepturi optio reprehenderit

2. 模拟服务端错误(500 错误,触发重试)

ini 复制代码
# 调用一个模拟500错误的接口(可自行搭建测试接口)
result = call_api_with_retry(
    url="https://httpstat.us/500",  # 该URL固定返回500
    method="GET",
    max_retries=2
)
# 输出日志:
# 服务端错误:500,准备重试(0/2)
# 重试间隔:1秒...
# 服务端错误:500,准备重试(1/2)
# 重试间隔:2秒...
# 已达最大重试次数(2次),请求失败

3. 模拟超时异常(触发重试)

ini 复制代码
# 调用一个模拟超时的接口(延迟15秒返回,超过设置的10秒超时)
result = call_api_with_retry(
    url="https://httpstat.us/200?sleep=15000",  # 延迟15秒
    method="GET",
    timeout=10,
    max_retries=1
)
# 输出日志:
# 请求超时(10秒):HTTPSConnectionPool(...), 准备重试(0/1)
# 重试间隔:1秒...
# 已达最大重试次数(1次),请求失败

五、最佳实践与扩展建议

1.** 日志记录 :将异常详情(时间、URL、错误信息)写入日志文件(如用logging模块),便于事后排查问题。
2.
动态调整重试策略 **:

  • 对核心接口(如支付接口)可提高重试次数(如 5 次),非核心接口降低重试次数(如 2 次)。
  • 根据接口响应时间动态调整超时时间(如正常响应 3 秒的接口,超时设为 5 秒)。
    3.** 资源释放 :若涉及文件上传或长连接,在异常处理中需确保资源正确关闭(如用with语句管理文件流)。
    4.
    告警机制 **:对高频失败的接口(如 10 分钟内失败 10 次)触发告警(如邮件、钉钉通知),及时介入处理。

通过上述异常处理框架,可有效应对 90% 以上的 API 调用问题,显著提升程序的健壮性和稳定性。核心是 "** 分类处理 + 重试策略 + 兜底机制 **",既保证错误可恢复,又避免无效重试浪费资源

相关推荐
前端日常开发20 分钟前
什么?你还不会PWA,那就让Trae来教吧~
trae
创码小奇客2 小时前
Spring Boot 集成 Talos:打造智能调参系统,让模型性能自动飙升
java·spring boot·trae
用户4099322502125 小时前
BackgroundTasks 如何巧妙驾驭多任务并发?
后端·github·trae
CF14年老兵9 小时前
2025 年每个开发人员都应该知道的 6 个 VS Code AI 工具
前端·后端·trae
程序员爱钓鱼12 小时前
Go语言实战案例:编写一个简易聊天室服务端
后端·go·trae
程序员爱钓鱼12 小时前
Go语言实战案例:实现一个并发端口扫描器
后端·go·trae
无责任此方_修行中1 天前
每日一技:当 Vuepress 插件失灵时,我是如何让 AI 帮我解决问题的
前端·vuepress·trae
程序员爱钓鱼1 天前
Go语言实战案例:TCP服务器与客户端通信
google·go·trae