requests异常处理需捕获具体异常而非仅Exception,分情况处理超时、连接错误、HTTP状态码;重试逻辑应使用for循环控制次数并确保最终抛出异常;响应处理前须校验status_code和text非None,手动指定编码更可靠;并发请求需主动捕获子任务异常并记录URL与参数。

requests请求失败时,except里只写Exception会掩盖真实问题
捕获所有异常看似稳妥,实则让调试变成盲人摸象。比如网络超时、DNS解析失败、SSL证书错误,全被Exception吞掉,日志里只剩一句“出错了”,根本看不出是该重试、换代理,还是该检查证书。
实操建议:
- 优先捕获具体异常:用
requests.exceptions.Timeout、requests.exceptions.ConnectionError、requests.exceptions.HTTPError分情况处理 -
HTTPError必须在Response.raise_for_status()之后才触发,别忘了调这个方法 - 兜底用
Exception可以,但至少打印type(e).__name__和str(e),不然等于没留线索
重试逻辑放错位置,导致无限循环或完全不重试
很多人把time.sleep()和continue塞进except里,结果发现不是重试了100次,就是一次都不重试——问题出在重试控制变量没放在循环作用域内,或者异常没被正确抛出。
实操建议:
- 用
for attempt in range(3):控制次数,别用while True+ 手动计数 - 每次请求前加
try,异常后if attempt == 2: raise,确保最后一次失败仍抛出 - 超时类异常(如
Timeout)适合重试;404、403这类HTTP状态码一般不该重试,要单独判断r.status_code
response.text解析失败,不是编码问题就是响应体为空
UnicodeDecodeError或AttributeError: 'NoneType' object has no attribute 'text'这两类报错,90%不是爬虫逻辑问题,而是没检查响应是否成功就直接取内容。
安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。
实操建议:
- 永远先写
if r.status_code == 200:再处理r.text,否则r.text可能为None - 不要依赖
r.encoding自动推断,用r.content.decode('utf-8', errors='ignore')更稳,尤其面对乱码HTML - 如果目标页面明确是UTF-8但
r.apparent_encoding返回ISO-8859-1,手动设r.encoding = 'utf-8'
多线程/协程下异常丢失,主线程悄无声息退出
用ThreadPoolExecutor或asyncio.gather()并发发请求时,某个任务出错不会中断整个流程,但如果你没显式收集Future.exception()或用return_exceptions=True,那个错误就像没发生过一样。
实操建议:
- 线程池中,用
future.result()主动取结果,它会重新抛出原始异常 - 协程中,
asyncio.gather(..., return_exceptions=True)后,遍历结果时用isinstance(r, Exception)判断是否失败 - 别在子任务里只打印异常而不抛出——上层根本收不到信号
最常被跳过的一步:在except块里没记录URL和请求参数。下次看到ConnectionError,你得先花两分钟翻日志确认到底是哪个链接挂了,而不是一眼扫到。异常处理的终点不是“程序不崩”,是“崩了也能立刻定位”。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










