捕获 httperror 必须用 urllib.error.httperror 且置于 urlerror 前,因其含 code、reason 和可读响应体;urlopen 默认对非2xx/3xx抛 httperror,需异常分支获取状态码。

捕获 HTTPError 时必须用 urllib.error.HTTPError 而非泛用 Exception
直接 except Exception 会吞掉 HTTPError,但你真正需要的是它——因为只有 HTTPError 实例才带 code 属性。它其实是 URLError 的子类,而 URLError 又继承自 IOError(Python 3 中为 OSError),所以顺序很重要:HTTPError 必须写在 URLError 前面,否则会被后者提前捕获。
- 错误写法:
except URLError:—— 会把404、500等全归为“网络错误”,拿不到状态码 - 正确顺序:
except HTTPError as e:→except URLError as e:→ (可选)except Exception: -
e.code就是服务器返回的 HTTP 状态码(如404、502),类型是int,不是字符串
HTTPError 实例同时提供 code、reason 和响应体
很多人只记得 e.code,却忽略 e.reason 是服务器返回的简短原因描述(如 'Not Found'),而响应体本身还能读取——只要没被关闭。注意:一旦调用 e.read(),后续再读会返回空字节,且不能重复使用。
-
e.code:状态码数字,例如401 -
e.reason:字符串,例如'Unauthorized' -
e.read():返回原始响应体(bytes),可用于解析 JSON 或 HTML;若需文本,建议用e.read().decode('utf-8') - 如果响应头中有
Content-Type,可据此判断是否该解码,比如text/html或application/json
用 urlopen 发起请求时,HTTPError 和正常响应共享同一异常路径
urllib.request.urlopen() 在收到非 2xx/3xx 响应时**默认抛出 HTTPError**,而不是返回一个带错误码的响应对象。这意味着你无法像 requests 那样用 r.status_code 判断——必须进异常分支才能拿到码。
- 没有“静默失败”模式;想让 4xx/5xx 不抛异常,得自己封装并捕获
HTTPError - 别指望
urlopen返回对象后检查属性——它根本不会返回,直接 raise - 若需统一处理逻辑(比如记录所有状态码),建议把请求逻辑包进函数,统一 try/catch
真实场景中容易漏掉的细节:重定向与 code 不一致
当启用重定向(urlopen 默认开启),最终抛出的 HTTPError 是**最后一个跳转响应的状态码**,不是初始请求的。比如 302 → 404,你拿到的是 404;但如果中间某步是 301,它不会触发 HTTPError(因 3xx 不报错),直到最后一步失败。
- 若需追踪全过程状态码,得禁用重定向:
urlopen(req, timeout=..., context=..., data=...) → 使用 <code>HTTPHandler自定义 opener,或改用urllib.request.build_opener()配合HTTPRedirectHandler子类 -
e.getcode()和e.code等价,但后者更常用;e.headers可访问响应头,对调试很有用 - 某些服务器返回
400却附带详细 JSON 错误信息,这时候e.read()比e.reason更有价值
code 看似简单,但它只在异常分支里存在,且和重定向、响应体读取强耦合——漏掉任一环节,就等于没真正拿到服务器想告诉你的东西。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











