3. Python 网络请求库:requests 库的安装与基本使用、Session 与 Cookie 管理、超时与重试机制
做数据采集,说白了就是跟 HTTP 打交道。Python 里最顺手的工具,我首推 requests 库。它比 Python 自带的 urllib 好用太多——代码更短,语义更清晰。今天我们就把它彻底聊透。
3.1 安装与第一行请求代码
安装没什么好说的,一行命令搞定:
pip install requests
我个人习惯在虚拟环境里装,避免污染全局。装好之后,验证一下:
import requests
print(requests.__version__)
能打出版本号,说明环境就绪了。
来,写第一个请求。抓一下百度首页:
import requests
url = "https://www.baidu.com"
response = requests.get(url)
print(response.status_code) # 200 表示成功
print(response.text[:200]) # 打印前200个字符
嗯,就这么简单。response 对象里封装了服务器返回的所有东西。状态码、响应头、响应体,全在里面。
http:// 或 https://。requests 不会自动补全,少了协议头会报错。养成好习惯,URL 写完整。
3.2 基本请求方法
HTTP 常用的几种方法,requests 都提供了对应函数:
| 方法 | requests 函数 | 典型场景 |
|---|---|---|
| GET | requests.get() |
获取资源,比如抓取网页 |
| POST | requests.post() |
提交数据,比如登录表单 |
| PUT | requests.put() |
更新资源 |
| DELETE | requests.delete() |
删除资源 |
| HEAD | requests.head() |
只获取响应头,不取内容 |
实际采集时,GET 和 POST 用得最多。带参数的 GET 请求可以这样写:
params = {"wd": "数据采集", "pn": 10}
response = requests.get("https://www.baidu.com/s", params=params)
print(response.url) # 实际请求的完整 URL
POST 请求传数据,用 data 参数:
data = {"username": "admin", "password": "123456"}
response = requests.post("https://httpbin.org/post", data=data)
print(response.json())
你想想看,用 data 传的是表单格式。如果接口要求 JSON,就用 json 参数:
import json
payload = {"key": "value"}
response = requests.post("https://httpbin.org/post", json=payload)
requests 会自动帮你把字典转成 JSON 字符串,并设置 Content-Type: application/json。这个细节我踩过坑——以前手动转 JSON 又忘了改请求头,接口一直返回 400。
3.3 Session 与 Cookie 管理
做采集最头疼的是什么?登录态。很多网站需要你先登录,拿到 Cookie,才能访问后续页面。
新手容易犯的错误:每次请求都重新建连接,Cookie 丢了。结果就是——明明登录成功了,下一页又让你登录。
requests 的 Session 对象就是解决这个问题的。它会自动保存 Cookie,并在后续请求中带上。
session = requests.Session()
# 第一步:登录
login_data = {"user": "test", "pass": "123"}
session.post("https://httpbin.org/post", data=login_data)
# 第二步:访问需要登录的页面
response = session.get("https://httpbin.org/cookies")
print(response.text)
看到没?用 session.get() 而不是 requests.get(),Cookie 就自动带上了。
有时候你需要手动设置 Cookie,比如从浏览器里复制过来的:
cookies = {"sessionid": "abc123", "token": "xyz789"}
response = requests.get("https://example.com/profile", cookies=cookies)
但我建议,能用 Session 自动管理的,就别手动折腾。我在项目中遇到过一种情况:某个网站的 Cookie 有效期很短,每次采集前都得重新登录。用 Session 配合登录流程,写个循环就能自动续期,省心很多。
3.4 超时与重试机制
网络请求最怕什么?卡死。一个请求发出去,服务器没响应,你的程序就停在那不动了。嗯,这就是没设超时。
requests 默认没有超时限制。也就是说,它可能一直等下去。我见过同事的采集脚本跑了三天,结果发现第二天凌晨网络断了,程序卡在某个请求上,后面全没执行。
设置超时很简单:
response = requests.get("https://httpbin.org/delay/5", timeout=3)
这里 timeout=3 表示 3 秒。如果 3 秒内服务器没响应,就会抛出 requests.exceptions.Timeout 异常。
更精细一点,可以分别设置连接超时和读取超时:
timeout = (3.05, 10) # 连接超时 3.05 秒,读取超时 10 秒
response = requests.get("https://httpbin.org/delay/5", timeout=timeout)
连接超时:建立 TCP 连接的最长等待时间。读取超时:连接建立后,等待服务器返回数据的最长时间。
超时之后怎么办?重试。手动写重试逻辑很烦,但 requests 配合 urllib3 的 Retry 模块,几行代码就能搞定:
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retry_strategy = Retry(
total=3, # 总重试次数
backoff_factor=1, # 退避因子,每次重试间隔递增
status_forcelist=[500, 502, 503, 504], # 哪些状态码触发重试
allowed_methods=["GET", "POST"] # 哪些方法可以重试
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("https://", adapter)
session.mount("http://", adapter)
# 现在用 session 发请求,会自动重试
response = session.get("https://httpbin.org/status/500")
这里有个关键参数 backoff_factor。它控制重试间隔:第一次重试等待 1 秒,第二次 2 秒,第三次 4 秒……指数递增。为什么要这样?如果服务器正在重启,你疯狂重试只会加重负担。退避策略给了服务器喘息的时间。
我曾经踩过一个坑:某个接口偶尔返回 503,我设了重试 5 次,backoff_factor 设成 0.1。结果重试间隔只有 0.1、0.2、0.4、0.8、1.6 秒,总共不到 3 秒就重试完了,服务器根本没恢复。后来我把 backoff_factor 改成 2,间隔变成 2、4、8、16、32 秒,成功率明显提升。
3.5 本章知识体系
下面这张图梳理了 requests 库的核心脉络,方便你对照复习:
3.6 避坑总结
最后,把我这些年用 requests 踩过的坑列出来,你遇到了可以直接翻回来对照:
- 忘了设超时——程序卡死,尤其是采集大量页面时,一个慢请求拖垮整个任务。
- 每次请求新建 Session——Cookie 不共享,登录态丢失。记住:一个采集任务用一个 Session 对象。
- 重试间隔太短——服务器还没恢复,你重试 10 次也没用。backoff_factor 设大一点。
- 忽略异常处理——网络请求一定会出异常。用
try/except捕获Timeout、ConnectionError、HTTPError。 - 响应体没关闭——大文件下载时,记得用
response.iter_content()流式读取,别一次加载到内存。