返回博客

Python Requests超时设置指南:GET、POST、Session与重试

Adrian Cole

2026年7月22日 · 教程 · 15 分钟阅读

Python Requests不会自动设置超时。本指南提供安全的GET和POST示例,解释连接超时和读取超时的行为,处理超时异常,设置真正的Session默认超时,添加适度的重试策略,并诊断直连与代理请求的差异。

Python Requests超时默认未启用。调用 requests.get()requests.post() 时不传 timeout,网络、远程服务或中间链路一旦长期无响应,程序可能一直等待。生产代码应为每个外部请求显式设置超时,并处理相应异常。

首先,我们要分清 timeout 限制的是什么。它不是整个请求加响应下载的总计时器,而是连接建立的等待时间,以及两次收到响应数据之间允许的最长间隔。单个数值会同时作用于两个阶段;元组可分别设置连接和读取超时。

本文会说明GET、POST、连接与读取超时、异常处理、Session默认值、有限重试和代理诊断。示例中的数值只用于说明写法;上线前请按实际服务的延迟和失败成本重新验证。

TL;DR

  • 除非显式传入超时参数,否则Requests不会应用超时。
  • 当连接建立和响应传输需要不同的时间限制时,使用 timeout=(connect, read)
  • 当连接阶段和读取阶段的失败需要不同恢复方式时,分别捕获对应的超时异常。
  • 不要把 timeout=5 当成整个请求生命周期的5秒硬性截止时间。

快速开始:安全的默认模式

下面是一个可直接运行的基础示例:

import requests

try:
    response = requests.get(
        "https://postman-echo.com/get",
        timeout=(3.05, 20),
    )
    response.raise_for_status()
    print(response.json()["url"])
except requests.exceptions.ConnectTimeout:
    print("建立连接耗时过长。")
except requests.exceptions.ReadTimeout:
    print("服务器在读取超时时间内停止发送数据。")
except requests.exceptions.RequestException as error:
    print(f"发生了其他 Requests 错误:{error}")

连接和读取需要不同预算时,用元组设置超时。若业务要求严格的总截止时间,需要在Requests外层用任务预算、进程边界或其他控制机制实现。

示例环境与前置条件

组件 测试值 用途
操作系统 Windows 11,内部版本26200 测试主机
Python 3.12.13 运行环境
Requests 2.34.2 HTTP客户端
urllib3 2.7.0 Requests使用的传输与重试支持
公共测试服务 Postman Echo 安全地回显GET和POST请求

下表是编写示例时使用的环境,不代表所有项目都应采用这些版本。固定依赖前,先核对Requests的PyPI发布信息 和项目自身的兼容性约束。建议在虚拟环境中安装和验证,避免影响其他项目:

python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install "requests==2.34.2"
python -c "import requests; print(requests.__version__)"

macOS或Linux可用 source .venv/bin/activate 激活环境。最后一条命令会输出已安装的Requests版本。升级依赖后,重新阅读官方文档并跑一遍示例。

发布前验证清单

  • 使用Postman Echo或你已获授权的测试端点,确认GET和POST示例能返回预期状态码。
  • 通过受控本地HTTP服务或测试环境复现读取超时;不要依赖公共延迟端点作为唯一验证手段。
  • 分别检查 session.timeout = ...、默认超时子类和 503 重试策略,并保留命令输出或CI记录。
  • 将实际Python、Requests和urllib3版本连同验证日期记录在发布工单中。

Python Requests的默认超时是多少?

Python Requests默认超时就是不设置超时。默认参数为 None,代码没有主动传值时,Requests不会限制连接或读取等待时间。Requests官方Quickstart的超时说明也建议显式传入该参数。

下面的调用没有Requests层面的超时:

import requests

response = requests.get("https://postman-echo.com/get")

传入单个数值,会将相同的数值同时应用于连接和读取阶段:

import requests

response = requests.get("https://postman-echo.com/get", timeout=10)

这不代表请求一定会在10秒内结束。主机名可能解析出多个IP,连接会依次尝试;读取超时限制的是两次收到数据之间的间隔,也不是大响应体的总下载时间。把它当作非活动超时,不要当作端到端截止时间。

为Python Requests GET请求设置超时

Python Requests GET超时直接传给 requests.get()raise_for_status() 也要单独调用:超时表示某个网络阶段等得太久,404429500 则表示已经收到了错误状态码的HTTP响应。

import requests

response = requests.get(
    "https://postman-echo.com/get",
    timeout=20,
)
response.raise_for_status()

print(response.status_code)
print(response.json()["url"])

若测试端点可用,预期输出类似:

200
https://postman-echo.com/get

不要把 20 原样复制到所有项目。根据服务的正常延迟和高分位延迟设置读取超时,并留出网络波动余量。面向用户的API和定时导出任务,所需的读取超时往往不同。

为Python Requests POST请求设置超时

Python Requests POST超时使用同一个参数。该方法不需要专门的超时API;requests.post() 会通过常规Requests调用链传递受支持的请求选项。

import requests

response = requests.post(
    "https://postman-echo.com/post",
    json={"task": "timeout-test"},
    timeout=(3.05, 20),
)
response.raise_for_status()

print(response.status_code)
print(response.json()["json"])

若测试端点可用,预期输出类似:

200
{'task': 'timeout-test'}

GET和POST使用相同的超时参数,重试时却不能一概而论。GET通常是幂等操作;客户端收不到响应时,POST可能已经创建订单、扣款或提交任务。没有幂等键或等效保护时,自动重试POST可能重复执行副作用。

拆分连接超时和读取超时

当建立连接和生成响应具有不同时间预算时,使用包含两个元素的元组:

response = requests.get(
    "https://postman-echo.com/get",
    timeout=(3.05, 20),
)

第一个数值是连接超时,限制每次向已解析IP建立套接字连接的等待时间。一个主机名可能解析出多个地址,底层会依次尝试,所以总连接耗时可能超过单次连接超时。

第二个数值是读取超时。连接建立、请求发出后,它限制两次收到响应字节之间最多能等多久。它常表现为等待首字节的时间,但不是总下载时长上限。服务端只要持续在间隔到期前发送少量数据,整个响应就可能超过设定的读取超时。

形式 含义 适用场景
timeout=10 对连接和读取非活动时间均应用10秒 两个阶段预算相近的简单调用
timeout=(3.05, 20) 每次连接尝试最多3.05秒;收到字节之间最多等待20秒 连接很快但服务端处理较慢的API
timeout=None 不应用Requests超时 仅限少见且经过审慎考虑的情形;不适合作为随意的默认值

Requests的超时元组不能提供严格的总截止时间。需要这一能力时,在更高层用任务预算、进程边界或队列控制实现。还要单独测试取消逻辑:停止一个等待中的线程,不等于取消异步操作。

捕获超时异常,同时不掩盖其他失败

Requests分别定义了连接和读取超时异常,也提供了共同父类。恢复逻辑或日志不同,就分别捕获:

import requests

try:
    response = requests.get(
        "https://postman-echo.com/get",
        timeout=(3.05, 20),
    )
    response.raise_for_status()
except requests.exceptions.ConnectTimeout:
    print("连接建立时间超过了连接超时限制")
except requests.exceptions.ReadTimeout:
    print("服务器未在读取超时时间内持续发送数据")
except requests.exceptions.RequestException as error:
    print(f"请求因其他原因失败:{error}")
else:
    print(response.status_code)

连接和读取超时走同一套处理流程时,捕获 requests.exceptions.Timeout 即可,它是 ConnectTimeoutReadTimeout 的父类。其后仍要保留 RequestException,避免把DNS失败、连接被拒绝、重定向、TLS或HTTP错误混为超时。

验证异常分支时,可让本地HTTP服务在发送响应前暂停0.25秒,并把读取超时设为0.05秒;预期会触发 ReadTimeout。公共 /delay 端点不适合作为唯一验证来源,因此成功示例使用Postman Echo。

为Session设置真正的默认超时

下面这种写法看似合理,实际不会生效:

session = requests.Session()
session.timeout = 10  # 这不会配置 Session.request()。

Python允许给对象添加这个属性,但Requests构建请求时不会读取它。可用一个响应延迟超过0.20秒的本地服务验证:即使设置 session.timeout = 0.05,请求仍会完成。

要设置默认值,可以封装一个小型Session子类,在真正的请求参数中注入 timeout

import requests


class TimeoutSession(requests.Session):
    def __init__(self, timeout=(3.05, 20)):
        super().__init__()
        self.default_timeout = timeout

    def request(self, method, url, **kwargs):
        kwargs.setdefault("timeout", self.default_timeout)
        return super().request(method, url, **kwargs)


with TimeoutSession() as session:
    response = session.get("https://postman-echo.com/get")
    response.raise_for_status()
    print(response.status_code)

setdefault() 会保留每次调用时显式传入的覆盖值:

with TimeoutSession() as session:
    response = session.get("https://postman-echo.com/get", timeout=(5, 60))
    response.raise_for_status()

调用方传入 timeout=None 时,会刻意关闭这个默认值。内部客户端若不允许无限等待,应显式拒绝 None

仅在操作安全时添加重试

Requests默认不重试失败连接。需要重试时,给 HTTPAdapter 挂载urllib3的 Retry 策略,并限制次数和允许的方法:

import requests
from requests.adapters import HTTPAdapter
from urllib3.util import Retry

retry_policy = Retry(
    total=2,
    status_forcelist=[429, 502, 503, 504],
    allowed_methods={"GET", "HEAD"},
    backoff_factor=0.5,
    respect_retry_after_header=True,
)

with requests.Session() as session:
    session.mount("https://", HTTPAdapter(max_retries=retry_policy))
    response = session.get(
        "https://postman-echo.com/get",
        timeout=(3.05, 20),
    )
    response.raise_for_status()

可用受控本地端点验证重试:第一次返回 503,第二次返回 200。按这套设定,预期最终状态为 200,服务端应记录两次请求。发布前保留实际运行结果。

重试不能替代超时设置。每多一次尝试都会消耗时间和服务端容量。遵守 Retry-After,限制重试次数;在分布式任务中加入抖动(jitter)。除非能证明操作幂等,否则不要把POST放进 allowed_methods

基于证据选择超时值

没有适用于所有场景的最佳Python HTTP超时值。超时应根据延迟分布、用户可接受的等待时间、响应体大小、网络路径和失败成本来定。

建议采用以下过程:

  1. 在正常负载下测量连接和响应耗时。
  2. 使用元组,将快速的连接建立与较慢的服务端处理分开。
  3. 测试高延迟地区,以及生产任务会使用的任何代理路径。
  4. 确定在该操作的总时间预算内可容纳多少次尝试。
  5. 记录超时阶段、URL主机、尝试次数、已耗时间和关联ID,但不要记录凭据或敏感响应数据。
  6. 当端点、地区、负载、代理或服务级目标发生变化时,重新审查这些值。

超时设得太短,正常波动也可能被误判为失败;设得太长,又会占住工作线程、推迟失败处理。具体取值要看业务预算,不能从教程里照搬一个数字。

排查Python HTTP请求超时失败

症状 可能原因 验证方式 实际处理措施
脚本看似卡住 未提供超时值 在调用前后立即记录时间戳 添加显式的单值或元组,并捕获超时
出现 ConnectTimeout 路由、防火墙、端点或连接路径缓慢或不可用 测试DNS解析、TCP可达性、其他网络和重复尝试 修复可达性;仅在测量后调整连接超时值
连接后出现 ReadTimeout 服务端处理或响应传输暂停 对比服务器日志、首字节时间、负载大小和直连请求 有依据时增加读取预算,或减少服务端工作量/响应体大小
已耗时间超过元组值 尝试了多个IP地址,或响应字节持续到达 记录解析出的地址和流式传输时间 不要把元组视为现实时间截止时间
只有经代理的调用超时 代理路由、地区、认证或端点健康状况不同 对同一个已获授权的请求分别直连和经代理发送;比较阶段与延迟 修复凭据或路由,选择更健康的端点,然后再调整数值
超时与 403429 同时出现 超时和访问/限流响应可能是两个独立问题 重试前记录状态码和相关响应头 降低请求频率,遵守规则,并按文档的访问要求执行
重试让任务更慢 尝试次数过多,或退避时间超过预算 记录尝试次数与累计耗时 降低重试次数、限制退避时间,并明确失败

不要为了解决超时而关闭TLS验证。verify=False 只会跳过证书校验,增加中间人攻击风险,解决不了连接或读取超时。

诊断代理和爬取工作流中的超时

在合规的公开网页数据收集中,超时可能出在目标服务、本地网络、代理连接或响应路径。排查时一次只改一个变量:先直连发送已获授权的请求,再用相同的URL、请求头、载荷和超时元组经代理发送。记录失败发生在连接还是读取阶段、已耗时间和HTTP状态。

直连稳定、代理失败时,先检查代理认证、端点位置、路由延迟、会话设置和IP健康状态,不要急着拉长超时。Rola IP提供产品背景;Python代理配置可参考Rola IP文档中心

下面用同一项已获授权请求比较直连和代理表现。真实凭据应从环境变量或密钥管理服务读取,不要写进代码库或日志。凭据含特殊字符时,先做URL编码。

import os
import requests

proxy_url = os.environ["HTTPS_PROXY_URL"]
proxies = {"http": proxy_url, "https": proxy_url}

response = requests.get(
    "https://postman-echo.com/get",
    proxies=proxies,
    timeout=(3.05, 20),
)
response.raise_for_status()
print(response.status_code)

记录直连和代理请求的连接/读取耗时、HTTP状态和关联ID,再决定要不要调整超时。不要靠提高并发、绕过访问限制或关闭TLS验证来掩盖路由问题。

代理不能替代超时处理、有限重试、速率限制和应用层错误报告,也不等于获得了数据收集许可。使用前仍需遵守目标网站条款、robots政策、合同限制、速率限制和适用法律。

timeout 不会强制执行的限制

Requests的 timeout 参数不会自动提供以下所有控制:

  • 对DNS解析、连接尝试、重定向和完整响应体合计时长的保证性截止时间;
  • 最大响应体大小;
  • 最大重定向次数,除非另行配置;
  • 对无关应用程序工作的自动取消;
  • 对非幂等操作的安全重试;
  • 防御服务器以足够慢但持续的速度发送字节、从而不断重置读取间隔的情况。

下载大文件时,使用流式传输;可用时校验 Content-Length,统计已接收字节数,并在超过大小或时间预算时停止。严格截止时间或高并发场景,选用具备总时长和取消机制的客户端或架构,不要把Requests当成异步运行时。

结论

Requests代码应显式设置超时;连接和读取的预算不同时,用元组拆开设置。不要把非活动超时误当作整个请求的截止时间。为超时写好异常处理,用请求参数实现Session默认值,只对安全的方法重试。

只有代理路径超时时,先与等价的直连请求对比,再改数值。这样能区分应用自身问题和特定路由的延迟问题。

常见问题

准备开始大规模采集数据了吗?

免费试用