1. HTTP协议基础:从0.9到3.0,我们到底在聊什么?

做数据采集这些年,我经常被问到同一个问题:「HTTP不就是发个请求收个响应吗?有什么好学的?」

嗯,这话对了一半。HTTP确实就干这两件事。但你要是真把它当黑盒用,踩坑是迟早的事。我见过太多采集脚本跑着跑着突然挂了,查半天发现是协议版本不兼容,或者没处理好连接复用。所以这一章,咱们先把地基打牢。

1.1 HTTP发展史:从一行文本到多路复用

HTTP从诞生到现在,经历了四个大版本。每个版本的出现,都是为了解决当时最头疼的问题。

版本 诞生年份 核心特点 我眼中的意义
HTTP/0.9 1991 只有GET,无首部,无状态码 原型验证,简陋但够用
HTTP/1.0 1996 引入首部、状态码、多种方法 真正可用的协议
HTTP/1.1 1997 持久连接、管道化、分块传输 统治互联网20年
HTTP/2 2015 二进制分帧、多路复用、头部压缩 性能飞跃
HTTP/3 2022 基于QUIC、0-RTT、无队头阻塞 面向移动和弱网

HTTP/0.9 时代,请求就一行:GET /index.html。服务器直接返回HTML内容,没有首部,没有状态码。你想想看,连「404」都没有,出错了就是空响应。我刚开始学HTTP时翻到这段历史,觉得这哪是协议,就是个对讲机。

HTTP/1.0 终于像样了。它引入了 Content-Type、Status-Code,还支持了POST和HEAD。但有个致命问题——每个请求都要新建TCP连接。一个网页如果有10张图片,就得建立10次TCP连接。这在1996年还能忍,放到今天,光握手时间就够用户骂街了。

HTTP/1.1 是我用得最多的版本。它最大的贡献是持久连接(Connection: keep-alive)。同一个TCP连接可以发多个请求,不用反复握手。还加了管道化(Pipelining),理论上可以连续发请求不用等响应。但说实话,管道化在实际项目中几乎没人用——因为队头阻塞问题太严重了。我曾经在一个采集项目里试过管道化,结果一个慢请求堵住了后面所有请求,还不如老老实实排队。

HTTP/2 彻底改了玩法。它把文本协议变成了二进制协议,引入了多路复用。什么意思?就是多个请求可以在同一个TCP连接上同时传输,互不干扰。还做了头部压缩(HPACK),减少了重复数据。我在采集高并发API时,从1.1切到2,吞吐量直接翻了一倍。

HTTP/3 是最近的事。它把底层的TCP换成了QUIC(基于UDP)。为什么要换?因为TCP的队头阻塞是根子里的问题——丢一个包,后面所有数据都得等。QUIC解决了这个问题,还支持0-RTT连接,说白了就是「秒连」。对于移动端采集和弱网环境,这是大杀器。

核心结论: 做数据采集,HTTP/1.1 和 HTTP/2 是当前主力。HTTP/3 正在普及,但很多中间件和代理还不支持。我建议你至少掌握1.1和2的区别,因为很多反爬机制会利用协议版本做检测。

1.2 HTTP协议特点:无状态、无连接

这两个特点,是理解HTTP一切行为的基础。

无状态:服务器不记得你是谁。每次请求都是独立的,服务器不会自动关联你上一次干了什么。为什么会这样?因为设计初衷就是简单——服务器只负责「收到请求→返回响应」,不保存客户端上下文。这样服务器可以轻松横向扩展,扛住海量请求。

但无状态也带来了麻烦。比如你登录了淘宝,刷新一下页面,服务器又不认识你了。怎么办?用Cookie和Session。Cookie把状态信息存在客户端,每次请求自动带上。Session把状态存在服务器,通过Session ID关联。我在采集需要登录的网站时,最常干的事就是模拟Cookie,把登录后的凭证塞进请求里。

无连接:HTTP/1.0时代,每次请求结束后TCP连接就断了。好处是服务器不用维护大量空闲连接,节省资源。坏处是每次请求都要重新建立连接,效率低。HTTP/1.1用持久连接解决了这个问题,但「无连接」的思想依然影响着协议设计——服务器随时可以关闭连接,客户端要做好重连的准备。

避坑指南: 我曾经写过一个采集脚本,没处理连接被服务器主动关闭的情况。跑了半小时,突然所有请求都超时了。查了半天,发现是服务器在空闲60秒后自动断开了连接。解决方案很简单:加上重试机制,并在请求头里设置 Connection: keep-alive。

1.3 URL结构详解:拆开看,其实很简单

URL(统一资源定位符)是HTTP世界的地址。你每天在浏览器地址栏里敲的东西,拆开来看就几个部分。

https://user:pass@api.example.com:8080/path/to/resource?key1=val1&key2=val2#fragment
|__|   |______| |_________________| |__| |_______________| |_________________| |______|
协议   用户信息        主机          端口        路径              查询参数        片段
组成部分 示例 说明
协议 https http 或 https,决定了数据传输方式
用户信息 user:pass 很少用了,现在基本用Authorization头
主机 api.example.com 域名或IP地址
端口 8080 HTTP默认80,HTTPS默认443
路径 /path/to/resource 服务器上的资源位置
查询参数 key1=val1&key2=val2 以?开头,&分隔键值对
片段 #fragment 不会发送到服务器,只在浏览器端使用

做数据采集时,我最关注的是查询参数。很多网站会把分页、排序、搜索条件放在这里。比如:?page=2&size=20&sort=time。我习惯用Python的 urllib.parse 来解析和构造URL,比手拼字符串靠谱多了。

注意: URL中的中文和特殊字符必须进行百分号编码(Percent-encoding)。比如中文「数据」会变成 %E6%95%B0%E6%8D%AE。我见过有人直接拼接中文URL,结果服务器返回400错误。记住:URL只支持ASCII字符集。

1.4 HTTP报文结构:请求行、状态行、首部、实体

HTTP报文就两种:请求报文和响应报文。结构非常对称,你记住一个,另一个就懂了。

请求报文

GET /api/data?page=1 HTTP/1.1          ← 请求行
Host: api.example.com                   ↓
User-Agent: Mozilla/5.0                 ← 首部
Accept: application/json                ↓
                                        ← 空行(必须)
                                        ← 实体(GET通常无实体)

请求行由三部分组成:方法、URI、协议版本。方法有GET、POST、PUT、DELETE等。URI是请求的资源路径(不含域名)。协议版本告诉服务器你用哪个版本的HTTP。

首部是键值对,每行一个。常见的如 Host、User-Agent、Cookie、Content-Type。首部可以分成几类:通用首部、请求首部、响应首部、实体首部。做采集时,User-Agent 和 Cookie 是最常需要伪造的。

空行是必须的。它用来分隔首部和实体。很多新手写HTTP请求时忘了加空行,结果服务器一直等实体,导致超时。我踩过这个坑,后来写代码时都会检查报文末尾有没有 \r\n\r\n。

实体是请求携带的数据。GET请求通常没有实体,POST请求把表单数据或JSON放在这里。

响应报文

HTTP/1.1 200 OK                        ← 状态行
Content-Type: application/json          ↓
Content-Length: 123                     ← 首部
Set-Cookie: session_id=abc123           ↓
                                        ← 空行(必须)
{"status": "ok", "data": [...]}         ← 实体

状态行包含协议版本、状态码、状态描述。状态码是三位数字,1xx表示信息,2xx表示成功,3xx表示重定向,4xx表示客户端错误,5xx表示服务器错误。做采集时,我最怕遇到429(Too Many Requests),说明被限流了。

首部和请求报文类似,但多了 Set-Cookie、Cache-Control 等响应特有的字段。

实体就是服务器返回的实际数据。可能是HTML、JSON、图片二进制流等。采集时根据 Content-Type 决定怎么解析。

我的习惯: 每次写采集脚本前,先用浏览器的开发者工具(F12 → Network)看一遍目标接口的请求和响应报文。确认请求行、首部、实体都长什么样,再动手写代码。这能省掉80%的调试时间。

1.5 本章知识体系总览

下面这张图,把本章的核心知识点串在了一起。你可以把它当路线图用。

HTTP协议基础 · 知识体系 HTTP协议基础 发展史 0.9 → 1.0 → 1.1 → 2 → 3 协议特点 无状态 · 无连接 URL结构 协议·主机·端口·路径·参数 报文结构 请求行/状态行 · 首部 · 空行 · 实体 核心:理解HTTP的「请求-响应」模型 以及每个版本解决了什么问题 这是后续学习采集技巧的基础

好了,HTTP协议的基础就讲到这里。记住:发展史让你知道「为什么这么设计」,特点让你理解「协议的天花板在哪」,URL和报文结构是你每天写代码都要面对的东西。把这些吃透了,后面讲请求方法、状态码、Cookie、代理的时候,你会觉得顺理成章。