kp-006

requests 与会话管理

核心 ≈ 25 分钟 requestsSessionCookie超时重试

前置知识

本文基于模型知识整理,建议核对官方文档(见 参考资料)。

一句话定义

requests 是 Python 事实标准的 HTTP 客户端;Session 对象在其上提供连接复用与 Cookie 自动携带,是正式采集脚本的标准容器。

为什么重要

裸 requests.get() 每次都是独立请求:不记 Cookie、不复用连接。一旦目标需要「先访问首页拿凭证再进列表页」,裸调用必然失败。会话思维是 kp-010(翻页)、kp-013(登录后采集)的共同前提。

前置知识

kp-002 的报文模型与状态码;kp-005 的环境。

核心概念

  • Session:跨请求保持 Cookie 与连接池的对象。
  • headers:UA、Referer、Accept-Language 是最常补的三件套。
  • timeout:(连接超时, 读取超时) 二元组;不设则可能永久挂起。
  • params / data / json:分别构造 URL 查询串、表单体、JSON 体。
  • resp.encoding:文本解码依据,乱码时手动指定。
  • proxies / verify:代理配置与证书校验开关。

原理与机制

Session 的价值 = 连接池复用(同一主机 TCP/TLS 握手一次)+ Cookie 罐自动「存取回传」。等效于浏览器「访问一次后所有请求自动带会话」的行为。

标准骨架(直接可复用):

import requests, time

HEADERS = {"User-Agent": "Mozilla/5.0 (Macintosh) learn-crawler/1.0"}

def fetch(session: requests.Session, url: str, **kw):
    for attempt in range(3):                     # 有限重试
        try:
            r = session.get(url, headers=HEADERS, timeout=(5, 20), **kw)
            r.raise_for_status()                 # 4xx/5xx 抛异常
            return r
        except requests.RequestException as e:
            wait = 2 ** attempt                  # 指数退避: 1s/2s/4s
            print(f"[retry {attempt}] {url} -> {e}, sleep {wait}s")
            time.sleep(wait)
    raise RuntimeError(f"failed after retries: {url}")

with requests.Session() as s:
    r = fetch(s, "https://books.toscrape.com/")
    r.encoding = "utf-8"
    print(len(r.text))

登录态维持的最小示例(练习站 quotes.toscrape.com):

s = requests.Session()
s.post("https://quotes.toscrape.com/login",
       data={"username": "demo", "password": "demo"}, timeout=(5, 20))
r = s.get("https://quotes.toscrape.com/", timeout=(5, 20))
# 登录后页面可见 premium 内容,Cookie 已由 Session 自动携带

实例或案例

采集脚本出现「本地能跑、挂机几小时后卡死」:根因几乎总是未设 timeout——某次连接半开导致永久阻塞。给所有请求统一加 (5, 20) 超时 + 有限重试,问题消失。timeout 是采集脚本的第一安全带。

常见误区

  • 误区一:不设 timeout。 requests 默认永不超时,生产事故高发第一号。
  • 误区二:每次 requests.get() 开新请求。 目标站依赖 Cookie 防重复时会被直接拦;也浪费握手开销。
  • 误区三:异常静默吞掉。 except: pass 会让 403 静默变成空数据,污染下游分析;至少记日志(见 kp-023)。

自测题

  1. Session 相比裸 get 的两大收益?

答:连接池复用降低握手开销;Cookie 自动维持会话(登录态、防重凭证)。

  1. timeout=(5, 20) 两个数字分别限制什么?

答:建立 TCP 连接的耗时上限 5 秒;从收到连接到读完响应体的耗时上限 20 秒。

  1. 为什么用 raise_for_status() 而不是只看状态码?

答:把 4xx/5xx 变成异常进入统一重试/日志通道,避免「返回错误页仍被当成功解析」。

与其他知识点的关系

kp-007 在本篇返回的 HTML 上做解析;kp-017 把这里的退避重试升级为系统化限速;kp-014 教你从抓包里找到该带哪些头。

延伸阅读

requests 官方文档「Advanced Usage — Session Objects」一节。

相关知识点

学习进度