3、HTTP状态码:从1xx到5xx,每一类都藏着什么秘密?

状态码这东西,说白了就是服务器给你回的一句「暗号」。做数据采集这几年,我见过太多新手一看到4xx、5xx就慌了神。其实没必要,状态码就是服务器在告诉你:你刚才那请求,我处理得怎么样。

我个人习惯把状态码分成五类来记。1xx是「还在处理」,2xx是「搞定了」,3xx是「你换个地儿」,4xx是「你搞错了」,5xx是「我搞砸了」。嗯,就这么简单。

核心原则:状态码是服务器对请求的「第一反馈」。采集程序必须根据状态码决定下一步动作——重试、跳转、还是直接报错。

HTTP 状态码分类全景 1xx 信息性 2xx 成功 3xx 重定向 4xx 客户端错误 5xx 服务器错误 临时响应 100 Continue 101 Switching 102 Processing 请求成功 200 OK 201 Created 204 No Content 资源移动 301 Moved 302 Found 304 Not Modified 请求有误 400 Bad Request 401 Unauthorized 403/404 服务异常 500 Internal 502 Bad Gateway 503 Service Unavail 采集程序的核心逻辑 2xx → 解析数据 | 3xx → 跟随跳转 | 4xx → 检查请求 | 5xx → 等待重试 每个状态码都是服务器给你的「操作指令」

1xx:信息性状态码——服务器说「我收到了,还在处理」

1xx 状态码在实际采集工作中其实很少见到。为什么?因为大多数 HTTP 客户端库(比如 Python 的 requests、Node 的 axios)都帮你处理好了,不会暴露给上层。

但有个场景我遇到过——做文件上传采集时,服务器返回了 100 Continue。意思是:你尽管发,我准备好了。这时候客户端才会继续发送请求体。

状态码 含义 采集场景
100 Continue 服务器已收到请求头,客户端可以继续发送请求体 大文件上传前的「握手」
101 Switching Protocols 服务器同意切换协议(如升级到 WebSocket) 实时数据流采集
102 Processing 服务器正在处理,但还没完成(WebDAV) 极少见,可忽略

我的建议:采集程序里不用专门处理 1xx。底层库已经帮你消化了。你只需要关注 2xx 及以上的状态码。

2xx:成功状态码——「搞定了,这是你要的数据」

2xx 是采集程序最喜欢的数字。看到 2xx,意味着请求被服务器正常处理了。但同样是 2xx,不同的码含义差别很大。

200 OK——最熟悉的陌生人

200 是最常见的成功状态码。表示请求成功,响应体里就是你要的数据。我采集过的网站,90% 以上的成功请求返回的都是 200。

# Python 示例:处理 200 响应
import requests

resp = requests.get('https://api.example.com/data')
if resp.status_code == 200:
    data = resp.json()  # 直接解析
    print(f"采集到 {len(data)} 条记录")
else:
    print(f"非预期状态码: {resp.status_code}")

201 Created——「你创建的东西,我存好了」

201 通常出现在 POST 请求之后。比如你向 API 提交了一条新数据,服务器创建成功,返回 201,同时在响应头 Location 里告诉你新资源的地址。

我记得有一次对接某个电商平台的商品上传接口,对方文档写的是返回 200,结果实际返回 201。排查了半天,才发现是文档没更新。嗯,这种事很常见。

204 No Content——「处理完了,但没啥可给你的」

204 有点特殊。服务器成功处理了请求,但响应体是空的。常见于 DELETE 操作,或者某些更新接口。

采集时遇到 204,别慌。这不是报错,只是服务器告诉你:事情办完了,但没有数据返回。你的程序应该继续,而不是抛出异常。

避坑指南:我曾经写过一个采集脚本,遇到 204 就直接报错退出。结果跑了三天,数据少了一半。后来才发现,那个接口在数据为空时返回 204,而不是 200。从那以后,我所有的采集程序都会单独处理 204——不解析响应体,但标记为「成功」。

3xx:重定向状态码——「你要的东西不在这儿,去那边找」

3xx 系列在采集工作中非常常见。说白了就是服务器告诉你:资源地址变了,你重新发个请求吧。

301 Moved Permanently——永久搬家

301 表示资源已经永久迁移到了新地址。采集程序遇到 301,应该更新记录的 URL,以后都用新地址。

我遇到过最典型的案例:某个政府数据网站改版,所有数据接口从 old.gov.cn/api 迁移到了 new.gov.cn/api。返回的全是 301。如果程序不处理重定向,第二天就全挂了。

302 Found——临时转移

302 表示资源临时在别处。下次请求还是用原地址。很多网站用 302 做临时跳转,比如登录后的页面跳转。

采集时要注意:requests 库默认会跟随重定向(allow_redirects=True)。但有时候你需要手动控制——比如想看看跳转前的 URL 是什么。

# 手动控制重定向
resp = requests.get('http://example.com/old-path', allow_redirects=False)
if resp.status_code in (301, 302):
    new_url = resp.headers['Location']
    print(f"跳转到: {new_url}")
    # 决定是否跟随

304 Not Modified——「没变,用缓存吧」

304 是个好东西。它表示资源没有更新,客户端可以直接用本地缓存。在采集场景下,合理利用 304 可以大幅减少请求量。

怎么用?发送请求时带上 If-Modified-Since 或 If-None-Match 头。如果资源没变,服务器返回 304,响应体为空。你的程序就不用重复解析了。

采集优化技巧:对于更新频率低的网站(比如新闻归档、历史数据),加上条件请求头。遇到 304 直接跳过,能省下 80% 的带宽和解析时间。

4xx:客户端错误——「你发的东西有问题」

4xx 是采集程序最常遇到的「非预期」状态码。看到 4xx,第一反应应该是:检查我的请求哪里错了。

状态码 含义 常见原因 采集对策
400 Bad Request 请求格式错误 参数缺失、JSON 格式不对 检查请求体/参数
401 Unauthorized 未认证 缺少 Token 或 Token 过期 刷新认证信息
403 Forbidden 禁止访问 IP 被封、权限不足 更换代理/检查权限
404 Not Found 资源不存在 URL 拼错、资源已删除 检查 URL/跳过

400 Bad Request——「你发的啥玩意儿?」

400 通常意味着请求语法有问题。比如 POST 请求的 JSON 格式不对,或者缺少必填参数。

我调试时遇到 400,第一件事就是打印出完整的请求头和请求体,跟 API 文档逐行比对。90% 的情况是字段名写错了。

401 vs 403——「没登录」和「没权限」

这两个容易混淆。401 是「你是谁?」,403 是「你是谁也不行」。

  • 401:没带 Token,或者 Token 过期了。重新登录获取新 Token 就行。
  • 403:服务器认识你,但你不配。比如普通用户想访问管理员接口,或者 IP 被拉黑了。

采集时遇到 403,我通常会先检查 User-Agent 和 Cookie。很多网站的反爬策略就是通过 403 来拒绝非浏览器请求。

404 Not Found——「找不着」

404 是最常见的「错误」状态码。但严格来说,它不算错误——服务器正常处理了请求,只是没找到资源。

采集时遇到 404,我的处理逻辑是:记录日志,跳过这条 URL,继续采集下一条。不要因为一个 404 就中断整个任务。

我曾经踩过的坑:某个数据源有 10 万条记录,其中 3% 的链接已经失效。第一次跑脚本时,遇到 404 就抛异常退出,结果只采集了 2000 条。后来改成「记录 + 跳过」,一天就跑完了全部有效数据。

5xx:服务器错误——「服务器自己炸了」

5xx 表示服务器端出了问题。这不是你请求的问题,是服务器自己的锅。采集程序遇到 5xx,正确的做法是等待后重试,而不是放弃。

状态码 含义 采集对策
500 Internal Server Error 服务器内部错误 等待 1-5 秒后重试,最多 3 次
502 Bad Gateway 网关/代理出错 通常短暂,等待后重试
503 Service Unavailable 服务暂时不可用 检查响应头 Retry-After,按指示等待

500 Internal Server Error——「服务器代码崩了」

500 是最通用的服务器错误。可能是数据库挂了,也可能是代码有 bug。采集时遇到 500,先别急着骂服务器——等几秒再试一次。

502 Bad Gateway——「上游挂了」

502 通常出现在有反向代理的场景。比如 Nginx 后面的应用服务器挂了,Nginx 就会返回 502。

我记得有一次采集某个大型电商平台,每到晚上 8 点就频繁出现 502。后来发现是对方服务器在高峰期扛不住了。我的对策是:遇到 502 就暂停 10 秒再重试,效果很好。

503 Service Unavailable——「别打了,我扛不住了」

503 是服务器主动告诉你:我现在太忙/正在维护,你过会儿再来。有些服务器会在响应头里加 Retry-After 字段,告诉你等多少秒。

# 处理 503 并遵循 Retry-After
import time
import requests

resp = requests.get('https://api.example.com/data')
if resp.status_code == 503:
    retry_after = int(resp.headers.get('Retry-After', 5))
    print(f"服务器忙,等待 {retry_after} 秒...")
    time.sleep(retry_after)
    # 重新请求

我的经验:5xx 错误大多数是暂时的。写采集程序时,一定要加重试机制。我一般用「指数退避」策略:第一次等 1 秒,第二次等 2 秒,第三次等 4 秒,最多重试 5 次。超过 5 次还报 5xx,那可能是对方服务器真的挂了,记录下来人工处理。

小结:状态码是服务器的「语言」

做数据采集,本质上就是在跟服务器对话。状态码就是服务器回应你的每一句话。听懂这些话,你的采集程序才能做出正确的反应。

记住这个口诀:2xx 直接拿数据,3xx 跟着走,4xx 查自己,5xx 等重试。