requests.get()默认不抛403/500异常,需显式调用raise_for_status()触发httperror;捕获时应区分4xx(如403,客户端问题,检查请求头、不盲目重试)和5xx(如500,服务端问题,可指数退避重试)。

403 和 500 不是“失败”,而是服务器明确告诉你:它收到了请求,也理解你要什么,但出于策略或状态原因,决定不给你结果。直接抛 HTTPError 或让程序崩溃,既不利于调试,也不利于重试控制。
requests.get() 抛出 HTTPError 怎么捕获才不漏关键信息
别只用 except Exception: 一锅端——HTTPError 是 URLError 的子类,必须显式捕获才能拿到 code、reason 和 headers 这三个有效字段。
- 用
except requests.exceptions.HTTPError as e:捕获,然后检查e.response.status_code(不是e.code,后者在某些旧版本里可能为空) -
e.response.headers很关键:有些站返回 403 时会在Retry-After或X-RateLimit-Remaining里埋线索;503 常带Retry-After,直接拿来 sleep - 不要忽略
e.response.text[:200]:部分反爬页会返回 HTML 提示(比如 Cloudflare 的“Checking your browser”),打印前 200 字符能快速识别是否被 WAF 拦截
403 和 500 的处理逻辑必须分开设计
403 是“你不像人”,500/503 是“它自己崩了”,响应策略完全不同。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- 对 403:优先检查请求头是否完整(
User-Agent、Referer、Accept-Language)、代理是否生效、UA 是否被标记;不建议立即重试,大概率越重试越快进黑名单 - 对 500/502/503:可安全重试(加
time.sleep(1–5)),尤其 503 带Retry-After头时,应严格按其值等待 - 对 429(Too Many Requests):必须退避,且不能只靠 sleep——要结合指数退避(如第 1 次等 1s,第 2 次等 2s,第 3 次等 4s),并记录触发次数,超阈值就暂停整个任务
用 Session + 钩子函数统一拦截错误响应
手动每个 requests.get() 都套 try-except 太重复。用 Session.hooks["response"] 注册钩子,集中处理常见错误码。
- 钩子里判断
response.status_code in [403, 500, 502, 503],做日志记录、触发告警、或调用预设的修复逻辑(如刷新 UA、切换代理) - 注意:钩子函数里不能直接修改
response对象,但可以 raise 自定义异常(如RatelimitError),再在外层统一捕获 - 避免在钩子里执行耗时操作(如写文件、发网络请求),否则拖慢所有请求;日志写入建议用异步队列或缓冲批量刷盘
真正容易被忽略的点是:403 响应体里可能藏着 Set-Cookie,而默认的 requests 异常流程不会解析它。如果你依赖 Cookie 维持登录态,却在 403 时丢弃了新 Cookie,下一次请求就会因会话失效再次 403——形成死循环。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










