HTTP 协议基础:请求与响应、方法、状态码、头部
说实话,做数据采集这行,HTTP 协议就是你的吃饭家伙。你写的每一个爬虫、推流的每一个请求,底层都在跟 HTTP 打交道。我见过太多新手拿着 requests 库咔咔一顿写,遇到 403、502 就懵了——说白了,就是没搞懂协议本身。
这一章,咱们把 HTTP 协议的核心掰开揉碎。不扯理论,只讲你写代码时真正用得上的东西。
HTTP 请求与响应:一次完整的对话
HTTP 协议本质上就是客户端和服务端之间的一次对话。客户端说“我要什么”,服务端回答“给你”或者“不给”。
我习惯把一次 HTTP 交互拆成两个部分来看:
- 请求(Request):客户端发给服务端的消息,包含“我想干什么”和“附带的信息”。
- 响应(Response):服务端返回给客户端的消息,包含“结果如何”和“你要的数据”。
举个例子,你在浏览器输入 https://api.example.com/data,浏览器会帮你构造一个 HTTP 请求,发出去。服务端处理完,返回一个响应。整个过程就这么简单。
核心要点:一个请求对应一个响应。没有响应,要么是网络断了,要么是服务端挂了。做采集时,如果发现请求发出去没回应,先检查这两点。
常见请求方法:GET 和 POST 的区别
HTTP 定义了很多方法,但咱们做数据采集和推流,最常用的就两个:GET 和 POST。其他的比如 PUT、DELETE,偶尔会遇到,但频率低得多。
| 特性 | GET | POST |
|---|---|---|
| 语义 | 获取资源 | 提交数据 |
| 参数位置 | URL 查询字符串(?key=value) | 请求体(Body) |
| 安全性 | 参数暴露在 URL 中,不安全 | 参数在 Body 中,相对安全 |
| 幂等性 | 是(多次请求结果相同) | 否(多次请求可能产生不同结果) |
| 数据长度限制 | 受 URL 长度限制(通常 2KB-8KB) | 理论上无限制 |
| 缓存 | 可以被浏览器缓存 | 通常不被缓存 |
我在项目中遇到过一个问题:有个同事用 GET 请求提交一个很大的 JSON 数据,结果服务端一直收不到完整数据。为什么?因为 URL 长度超了,数据被截断了。后来改成 POST,问题立刻解决。
我的建议:采集数据时,如果只是拉取列表、详情页,用 GET。如果需要提交表单、上传文件、发送大量数据,用 POST。别混用,也别为了省事把 POST 的参数塞到 URL 里。
状态码:服务端在说什么?
状态码是服务端给你的“暗号”。看懂它,你就能快速定位问题。我把它分成五类:
- 1xx(信息):服务端收到了,还在处理。日常采集基本见不到。
- 2xx(成功):请求成功了。最常见的是
200 OK。 - 3xx(重定向):资源挪地方了。比如
301永久重定向,302临时重定向。做采集时,遇到 302 要跟着跳转,否则拿不到真实数据。 - 4xx(客户端错误):你发的东西有问题。比如
400参数错误,401未认证,403禁止访问,404资源不存在。 - 5xx(服务端错误):服务端出问题了。比如
500内部错误,502网关错误,503服务不可用。
避坑指南:我曾经写过一个采集脚本,连续遇到 403 状态码。我以为是 IP 被封了,折腾了半天代理。后来仔细一看,是请求头里忘了加 User-Agent。记住:403 不一定是封 IP,也可能是请求头不对。
下面这张图帮你快速定位常见状态码的处理方式:
请求头与响应头:藏在背后的信息
HTTP 头部(Headers)是请求和响应里的“元数据”。它们不直接承载业务数据,但决定了请求能不能成功、响应该怎么解析。
我习惯把请求头分成三类:
- 身份标识类:
User-Agent(你是谁)、Referer(从哪来)、Cookie(会话信息)。做采集时,这三个头经常需要伪造。 - 内容协商类:
Accept(你要什么格式)、Accept-Language(你要什么语言)、Content-Type(你发的是什么格式)。 - 控制类:
Authorization(认证令牌)、Cache-Control(缓存策略)、Connection(连接管理)。
响应头里,我最关注这几个:
Content-Type:返回的数据是什么格式。是 JSON、HTML 还是图片?决定了你怎么解析。Set-Cookie:服务端让你保存的 Cookie。下次请求要带上。Location:重定向时告诉你去哪。Retry-After:限流时告诉你多久后再试。
一个小技巧:我写采集脚本时,习惯先用浏览器的开发者工具(F12)看一遍请求头。把关键的 Headers 复制出来,直接用到代码里。这样能避免很多“为什么我请求失败”的问题。
一个完整的 HTTP 请求示例
下面是一个用 Python 的 requests 库发送 GET 请求的例子。注意看我是怎么设置请求头的:
import requests
url = "https://api.example.com/users"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept": "application/json",
"Authorization": "Bearer your_token_here"
}
response = requests.get(url, headers=headers)
# 检查状态码
if response.status_code == 200:
data = response.json()
print("采集成功,数据条数:", len(data))
elif response.status_code == 403:
print("权限不足,检查 Token 或请求头")
elif response.status_code == 429:
print("请求太频繁,等待后重试")
else:
print(f"未知错误:{response.status_code}")
这段代码里,我手动设置了三个请求头。为什么?因为很多 API 服务会检查 User-Agent,没有的话直接拒绝。Authorization 是认证用的,不带它就拿不到数据。
我曾经踩过的坑:有一次我写推流脚本,往服务端 POST 数据,服务端一直返回 400。我检查了半天,发现是 Content-Type 没设置。默认 requests 会帮你设成 application/x-www-form-urlencoded,但服务端要的是 application/json。加上 headers={"Content-Type": "application/json"} 后,问题解决。
小结
HTTP 协议不难,但细节多。记住三点:
- 请求方法选对(GET 拿数据,POST 发数据)
- 状态码看懂(2xx 成功,4xx 你的问题,5xx 对方的问题)
- 请求头别漏(User-Agent、Content-Type、Authorization 是三大护法)
把这些基础打牢,后面的采集和推流实战,你会顺手很多。