2、HTTP协议基础:请求与响应、状态码、请求头与响应头、Cookie与Session

做数据采集,说白了就是跟HTTP协议打交道。你写的爬虫,本质上就是一个HTTP客户端。服务器不认识你是谁,它只认协议。所以,搞懂HTTP协议,是爬虫工程师的基本功。

我个人习惯把HTTP协议比作「快递服务」。你(客户端)填一张单子(请求),快递员送到仓库(服务器),仓库给你回一张回执(响应)。就这么简单。但细节里全是坑,咱们一个一个说。

2.1 请求与响应:一次完整的对话

一次HTTP交互,就两件事:你问,它答。

请求(Request):客户端发给服务器的消息。包含三部分:

  • 请求行:方法 + URL + 协议版本。比如 GET /index.html HTTP/1.1
  • 请求头:一堆键值对,告诉服务器你的「身份」和「偏好」
  • 请求体:POST请求时携带的数据,比如表单内容

响应(Response):服务器返回给客户端的消息。也分三部分:

  • 状态行:协议版本 + 状态码 + 状态描述。比如 HTTP/1.1 200 OK
  • 响应头:服务器告诉你它的「脾气」和「附加信息」
  • 响应体:真正的内容,HTML、JSON、图片二进制流都在这里

核心要点:爬虫拿到的数据,99%都在响应体里。但请求头和响应头里的信息,往往决定了你能不能拿到数据。

我在项目中遇到过一件事:一个爬虫明明URL写对了,但就是返回空数据。查了半天,发现是请求头里少了一个 Accept-Language 字段。服务器根据这个字段判断用户语言偏好,没传就直接返回默认页面。嗯,这种坑踩过一次就记住了。

2.2 状态码:服务器的「暗号」

状态码是服务器给你的「暗号」。三位数字,告诉你请求的结果。你想想看,如果服务器每次都用中文回复「成功」「失败」「找不到」,那解析起来多麻烦?用数字多干脆。

状态码 含义 爬虫中常见场景
200 请求成功 正常拿到数据
301/302 重定向 URL变了,需要跟进新地址
403 禁止访问 被反爬了,需要加headers或代理
404 资源不存在 URL写错了,或者页面被删了
429 请求过多 触发频率限制,需要加延时
500 服务器内部错误 服务器崩了,等会儿再试
503 服务不可用 服务器过载,需要重试策略

避坑指南:我曾经写过一个爬虫,只判断了200状态码。结果某天数据全没了,排查发现服务器返回了302重定向,但我的代码没处理,直接跳过了。从那以后,我所有爬虫都会处理301/302,并且对非200状态码做日志记录。

为什么会这样?因为很多网站为了防爬,会故意返回302让你跳转到验证页面。你不处理,就永远拿不到真实数据。

2.3 请求头与响应头:藏在「信封」上的信息

请求头和响应头,就像快递单上的寄件人和收件人信息。看似不起眼,但缺了哪一项都可能出问题。

常用请求头

  • User-Agent:告诉服务器你是什么浏览器。我建议至少准备5-10个不同的UA轮换使用
  • Referer:告诉服务器你从哪个页面跳过来的。很多图片站和视频站会校验这个字段
  • Cookie:携带登录状态或会话信息。后面会细说
  • Accept:告诉服务器你希望接收什么格式的数据。比如 application/json
  • Authorization:携带Token或API密钥,用于接口鉴权

常用响应头

  • Set-Cookie:服务器让你保存的Cookie,下次请求要带上
  • Content-Type:响应体的格式。比如 text/htmlapplication/json
  • Content-Length:响应体的大小,单位字节
  • Cache-Control:缓存策略。比如 no-cache 表示不要缓存
  • Location:重定向时的新地址

小技巧:我个人习惯在爬虫里先手动复制浏览器的完整请求头,然后逐步精简。你会发现有些字段是必须的,有些是可有可无的。比如 Accept-Encoding 如果设置不当,服务器返回压缩数据,你忘了解压就会拿到乱码。

举个例子,你写爬虫请求一个API接口:

import requests

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    'Referer': 'https://example.com/data-page',
    'Accept': 'application/json, text/plain, */*',
    'Cookie': 'session_id=abc123; user_token=xyz789'
}

response = requests.get('https://api.example.com/data', headers=headers)
print(response.json())

你看,就这么几行代码,但少了任何一个header,都可能返回403或者空数据。我在项目中遇到过最离谱的一次,是服务器校验 Accept-Language 字段,没传就返回英文版页面,导致解析规则全废了。

2.4 Cookie与Session:记住你是谁

HTTP协议本身是「无状态」的。什么意思?就是服务器记不住你。你第一次请求和第二次请求,在服务器看来是两个人。那怎么解决?靠Cookie和Session。

Session:存在服务器端。你登录后,服务器给你分配一个Session ID,存着你的登录状态、购物车信息等。

Cookie:存在客户端(浏览器)。服务器通过 Set-Cookie 响应头,让你把Session ID存下来。下次请求时,你带上这个Cookie,服务器就知道「哦,是你啊」。

核心要点:爬虫要维持登录状态,本质上就是维护好Cookie。你拿到一次登录后的Cookie,只要不失效,就可以一直用。

为什么会这样?因为很多网站的反爬机制,就是通过Session来检测的。比如你短时间内用同一个Session ID请求了1000次,服务器直接封你。所以,爬虫里管理Cookie是个技术活。

爬虫中Cookie的三种处理方式

  1. 手动复制:浏览器登录后,从开发者工具里复制Cookie字符串,直接写死在代码里。简单粗暴,但有效期短
  2. 自动管理:用 requests.Session() 自动处理Cookie。第一次登录后,后续请求自动携带
  3. 持久化存储:把Cookie存到文件或数据库里,下次启动爬虫时加载。适合长期运行的爬虫

我个人习惯用第二种,配合第一种做兜底。举个例子:

import requests

# 创建一个Session对象,自动管理Cookie
session = requests.Session()

# 第一步:登录,获取Cookie
login_data = {'username': 'admin', 'password': '123456'}
login_resp = session.post('https://example.com/login', data=login_data)

# 第二步:用同一个Session请求需要登录的页面
data_resp = session.get('https://example.com/my-data')
print(data_resp.text)

你看,用 requests.Session() 之后,你根本不用手动处理Cookie。它会自动从响应头里提取 Set-Cookie,然后在后续请求中自动带上。嗯,这里要注意:如果服务器返回了多个 Set-Cookie,Session对象也会帮你全部处理好。

避坑指南:我曾经遇到过一个网站,它的Cookie里有个 expires 字段,过期时间很短。我的爬虫跑着跑着就突然返回登录页面了。排查发现是Cookie过期了。解决方案是:在爬虫里加一个心跳请求,每隔几分钟访问一次首页,刷新Cookie的有效期。

另外,有些网站会把Session ID放在URL参数里,而不是Cookie里。这种叫「URL重写」。遇到这种情况,你需要从响应内容或重定向URL里提取Session ID,然后拼接到后续请求中。虽然少见,但碰到了别慌。

好了,HTTP协议基础就这些。说白了就是:请求怎么写,响应怎么读,状态码怎么处理,头信息怎么配,Cookie怎么管。把这些搞明白,你的爬虫就成功了一半。下一章咱们聊聊更具体的——如何用Python的requests库把这些理论落地。