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 就自动带上了。

核心理解: Session 对象在底层维护了一个 CookieJar。你发请求时,它自动把服务器返回的 Set-Cookie 存进去;下次请求时,自动把对应的 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 连接的最长等待时间。读取超时:连接建立后,等待服务器返回数据的最长时间。

注意: 超时时间别设太短。有些接口响应慢,比如导出报表、批量查询。设个 1 秒超时,大概率一直报错。我一般设 10-30 秒,具体看接口的响应速度。

超时之后怎么办?重试。手动写重试逻辑很烦,但 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 库的核心脉络,方便你对照复习:

requests 库核心知识体系 requests 库核心知识体系 requests 库 安装与基本使用 pip install requests GET / POST / PUT / DELETE Session 与 Cookie 管理 自动保存 Cookie 手动设置 Cookie CookieJar 机制 超时与重试机制 连接超时 / 读取超时 Retry + backoff_factor status_forcelist 核心要点 1. 用 Session 管理登录态,避免重复登录 2. 必须设超时,防止程序卡死 3. 重试配合退避策略,别暴力重试

3.6 避坑总结

最后,把我这些年用 requests 踩过的坑列出来,你遇到了可以直接翻回来对照:

  • 忘了设超时——程序卡死,尤其是采集大量页面时,一个慢请求拖垮整个任务。
  • 每次请求新建 Session——Cookie 不共享,登录态丢失。记住:一个采集任务用一个 Session 对象。
  • 重试间隔太短——服务器还没恢复,你重试 10 次也没用。backoff_factor 设大一点。
  • 忽略异常处理——网络请求一定会出异常。用 try/except 捕获 Timeout、ConnectionError、HTTPError。
  • 响应体没关闭——大文件下载时,记得用 response.iter_content() 流式读取,别一次加载到内存。
我的习惯: 写采集脚本时,我会先封装一个带超时和重试的 Session 工具函数。所有请求都走这个函数,统一管理。这样代码干净,出问题也好排查。