第3章 HTTP协议与网络请求:数据采集的“敲门砖”
说实话,做数据采集这么多年,我见过太多人一上来就写爬虫,结果被反爬搞得焦头烂额。其实问题的根子,往往出在没搞懂HTTP协议上。这一章,咱们就把这块“敲门砖”彻底敲明白。
3.1 HTTP/HTTPS协议基础——你发的请求,服务器到底怎么理解?
HTTP协议,说白了就是客户端和服务器之间的一种“对话规则”。你发一个请求,服务器回一个响应,就这么简单。但这里面有几个关键点,我当年踩过不少坑。
3.1.1 请求与响应的基本结构
一个HTTP请求,由三部分组成:请求行、请求头、请求体。响应也一样:状态行、响应头、响应体。
| 组成部分 | 请求 | 响应 |
|---|---|---|
| 起始行 | GET /api/data HTTP/1.1 | HTTP/1.1 200 OK |
| 头部 | Host、User-Agent、Cookie等 | Content-Type、Set-Cookie等 |
| 空行 | 分隔头部和体 | 分隔头部和体 |
| 体 | POST提交的表单数据 | HTML、JSON、图片等 |
嗯,这里要注意:GET请求没有请求体,所有参数都拼在URL里。POST请求的参数放在请求体里,相对安全一些。
3.1.2 状态码——服务器在跟你说什么?
我个人习惯,看到状态码先看百位数字:
- 2xx:成功。200是标准成功,201是创建成功(POST常用)。
- 3xx:重定向。301永久搬家,302临时跳转。我记得有一次爬一个网站,数据一直不对,后来发现是301重定向到了另一个URL。
- 4xx:客户端错误。404最常见,403是禁止访问(反爬了),429是请求太频繁(限流了)。
- 5xx:服务端错误。500内部错误,502网关超时。遇到5xx,我一般会等几秒重试。
3.1.3 HTTPS——多了个“S”到底多了什么?
HTTPS = HTTP + SSL/TLS。说白了,就是在HTTP外面套了一层加密协议。你想想看,如果不用HTTPS,你提交的密码、Cookie都是明文传输的,随便一个中间人就能截获。
用Requests库请求HTTPS网站时,默认会验证SSL证书。如果遇到证书过期或自签名的网站,可以设置 verify=False,但会收到一个警告。我个人建议:生产环境不要关闭验证,开发调试时可以临时关闭。
import requests
# 关闭SSL验证(仅用于调试)
response = requests.get('https://example.com', verify=False)
# 或者使用自定义证书
response = requests.get('https://example.com', cert=('/path/to/cert.pem', '/path/to/key.pem'))
3.2 Requests库的GET与POST方法——最常用的两个武器
Requests库是我用过的Python HTTP库里最顺手的。它把底层的urllib封装得特别优雅,代码写起来像说话一样自然。
3.2.1 GET请求——拿数据
GET请求,说白了就是“给我看看”。参数直接拼在URL后面,用 ? 开头,& 分隔。
import requests
# 最简单的GET请求
response = requests.get('https://api.github.com/users/octocat')
print(response.status_code) # 200
print(response.json()) # 自动解析JSON
# 带参数的GET请求
params = {'q': 'python', 'page': 1}
response = requests.get('https://api.github.com/search/repositories', params=params)
print(response.url) # 自动拼接参数到URL
这里有个小技巧:params 参数会自动处理URL编码。比如中文、特殊字符,都不用你手动转义,Requests帮你搞定。
3.2.2 POST请求——提交数据
POST请求,就是“我给你点东西”。数据放在请求体里,常见的有两种格式:表单数据和JSON数据。
import requests
# 提交表单数据
form_data = {'username': 'admin', 'password': '123456'}
response = requests.post('https://httpbin.org/post', data=form_data)
# 提交JSON数据
json_data = {'name': '张三', 'age': 30}
response = requests.post('https://httpbin.org/post', json=json_data)
data 和 json 参数不能混用。如果你传了 data,Requests会自动设置 Content-Type: application/x-www-form-urlencoded;如果传了 json,会自动设置 Content-Type: application/json。搞错了,服务器可能解析不了。
3.3 处理Cookies与Session——让服务器记住你
HTTP协议本身是无状态的。什么意思?就是服务器每次收到请求,都不知道你是谁。那怎么让服务器记住你呢?靠Cookie和Session。
3.3.1 Cookies——服务器留在你身上的“小纸条”
服务器通过 Set-Cookie 响应头,把一个小纸条贴在你身上。你下次请求时,带上这个纸条,服务器就知道你是谁了。
import requests
# 手动设置Cookie
cookies = {'session_id': 'abc123'}
response = requests.get('https://httpbin.org/cookies', cookies=cookies)
# 从响应中获取Cookie
response = requests.get('https://httpbin.org/cookies/set?name=value')
print(response.cookies) # <RequestsCookieJar>
3.3.2 Session对象——自动管理Cookie
手动管理Cookie太麻烦了。用 requests.Session(),它会自动帮你保存和发送Cookie。
import requests
# 创建Session对象
session = requests.Session()
# 第一次请求,服务器设置Cookie
session.get('https://httpbin.org/cookies/set?session_id=xyz')
# 第二次请求,自动带上Cookie
response = session.get('https://httpbin.org/cookies')
print(response.json()) # 可以看到session_id=xyz
我个人习惯:只要是需要登录的网站,一律用Session。它还能保持TCP连接,减少握手开销,性能更好。
3.4 设置请求头与代理——伪装与绕行
做数据采集,最怕被反爬。设置合理的请求头和使用代理,是绕过反爬的基本功。
3.4.1 请求头——让请求看起来像真人
服务器会检查请求头来判断你是不是爬虫。最常见的检查项是 User-Agent,也就是“你用什么浏览器访问的”。
import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Referer': 'https://www.google.com/',
'Accept-Language': 'zh-CN,zh;q=0.9',
}
response = requests.get('https://httpbin.org/headers', headers=headers)
Accept-Encoding、Accept 等头,最好都模拟成浏览器的样子。
3.4.2 代理——换个IP再来
同一个IP访问太频繁,肯定会被封。用代理IP,可以分散请求来源。
import requests
# 使用HTTP代理
proxies = {
'http': 'http://127.0.0.1:8080',
'https': 'http://127.0.0.1:8080',
}
response = requests.get('https://httpbin.org/ip', proxies=proxies)
# 使用SOCKS5代理(需要安装requests[socks])
proxies = {
'http': 'socks5://127.0.0.1:1080',
'https': 'socks5://127.0.0.1:1080',
}
response = requests.get('https://httpbin.org/ip', proxies=proxies)
3.5 本章知识体系
下面这张图,把本章的核心知识点串起来了。你想想看,从协议基础到实际编码,再到反爬对抗,其实是一条线。
3.6 实战小案例:模拟登录一个简单网站
光说不练假把式。咱们用Session + 请求头 + POST,模拟登录一个测试网站。
import requests
# 1. 创建Session
session = requests.Session()
# 2. 设置请求头,模拟浏览器
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Referer': 'https://httpbin.org/forms/post',
}
session.headers.update(headers)
# 3. 先GET一下登录页面,获取Cookie
session.get('https://httpbin.org/cookies/set?session_id=test123')
# 4. POST提交登录数据
login_data = {'username': 'admin', 'password': 'secret'}
response = session.post('https://httpbin.org/post', data=login_data)
# 5. 检查结果
print('状态码:', response.status_code)
print('响应内容:', response.json())
print('当前Cookie:', session.cookies.get_dict())
你看,整个过程就是:建立会话 → 伪装浏览器 → 获取Cookie → 提交数据。这套模式,能应对80%的简单网站。
is_login() 函数,定期检查Cookie是否过期,过期就重新登录。