在 Python 的 requests 库中,Session 对象是一个非常重要的概念,它允许你在多个请求之间保持某些参数和状态。

Session 的基本概念

Session 对象能够:

  • 在多个请求之间保持 cookies

  • 保持连接池,提高性能

  • 保持某些请求参数(如 headers、auth 等)

使用 Session 的优势

  1. 性能提升:重用底层 TCP 连接,减少连接建立的开销

  2. 保持状态:自动处理 cookies,无需手动管理

  3. 统一配置:可以为所有请求设置默认参数

基本用法

import requests

# 创建一个 Session 对象
s = requests.Session()

# 设置 Session 级别的参数
s.headers.update({'User-Agent': 'MyApp/1.0'})

# 使用 Session 发起请求
response = s.get('https://httpbin.org/get')

# 关闭 Session (通常不需要手动关闭,除非明确需要)
s.close()

常用功能

1. 保持 Cookies

s = requests.Session()
s.get('https://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get('https://httpbin.org/cookies')
print(r.text)  # 会显示之前设置的 cookie

2. 设置默认参数

s = requests.Session()
s.auth = ('user', 'pass')
s.headers.update({'x-test': 'true'})

# 这些参数会自动应用到所有请求
r = s.get('https://httpbin.org/headers', headers={'x-test2': 'true'})

3. 连接池管理

Session 会自动管理连接池,默认情况下会重用连接。

上下文管理器用法

with requests.Session() as s:
    s.get('https://httpbin.org/cookies/set/sessioncookie/123456789')
    # 离开 with 块后 Session 会自动关闭

Session.request() 方法

requests 库的核心请求处理逻辑集中在 sessions.py 的 Session.request() 方法中。下面将 逐层深入分析 该方法的实现细节,从参数处理到最终发送请求的全流程。

1. 方法签名

def request(self, method, url, params=None, data=None, headers=None, cookies=None,
           files=None, auth=None, timeout=None, allow_redirects=True, proxies=None,
           hooks=None, stream=None, verify=None, cert=None, json=None):
  • 核心参数

    • method: HTTP 方法(GET/POST等)

    • url: 请求地址

    • params: URL 查询参数(自动编码)

    • data: 表单数据(字典或字节)

    • json: JSON 请求体(自动序列化)

    • headers: 自定义请求头

    • timeout: 超时时间(秒)

    • proxies: 代理配置

2. 执行流程分解

步骤 1:合并配置
# 合并会话级配置和本次请求的局部配置
headers = merge_setting(headers, self.headers)
proxies = merge_setting(proxies, self.proxies)
  • 会话级配置(如 self.headers)会被本次请求的 headers 参数覆盖。

步骤 2:构造 Request 对象
req = Request(
    method=method.upper(),
    url=url,
    headers=headers,
    files=files,
    data=data or {},
    json=json,
    params=params or {}
)
  • Request 类(定义在 models.py)封装原始请求信息。

步骤 3:预处理请求
prep = self.prepare_request(req)
  • prepare_request() 关键操作

    1. 参数编码

      if params:  # 编码 URL 查询参数
          url = requote_uri(url + '?' + urlencode(params))
    2. 数据序列化

      if json is not None:  # 自动序列化 JSON
          body = json.dumps(json)
          headers['Content-Type'] = 'application/json'
    3. 文件上传处理

      if files:  # 生成 multipart/form-data
          body, content_type = encode_multipart_formdata(files)
          headers['Content-Type'] = content_type
步骤 4:选择适配器
adapter = self.get_adapter(url=url)
  • 适配器选择逻辑

    • 根据 URL 的协议(http:// 或 https://)选择对应的 HTTPAdapter

    • 默认使用 urllib3 作为底层实现。

步骤 5:发送请求
resp = adapter.send(
    request=prep,
    stream=stream,
    timeout=timeout,
    verify=verify,
    cert=cert,
    proxies=proxies
)
  • adapter.send() 关键操作

    1. 连接池管理

      conn = self.poolmanager.connection_from_url(url)  # 从池中获取连接
    2. 发送 HTTP 请求

      urllib3_response = conn.urlopen(
          method=request.method,
          url=url,
          body=request.body,
          headers=request.headers,
          timeout=timeout
      )
    3. 处理重定向(若 allow_redirects=True):

      while 'location' in response.headers:
          next_url = response.headers['location']
          response = adapter.send(next_url)  # 递归发送新请求
步骤 6:构建 Response 对象
response = self.build_response(prep, resp)
  • build_response() 关键操作

    response = Response()
    response.status_code = urllib3_response.status
    response.headers = CaseInsensitiveDict(urllib3_response.headers)
    response.raw = urllib3_response  # 原始 urllib3 响应
    response.request = prep          # 关联原始请求

3. 关键子方法解析

3.1 merge_setting()(配置合并)
  • 优先级:局部参数 > 会话级配置 > 全局默认值。

  • 示例

    session = Session()
    session.headers.update({'User-Agent': 'MyApp/1.0'})
    # 本次请求使用特殊 UA
    session.get(url, headers={'User-Agent': 'Crawler'})
3.2 prepare_request()(请求预处理)
  • 核心任务

    • 序列化 JSON 数据

    • 编码 URL 参数

    • 生成 multipart 文件上传体

  • 源码位置sessions.py 的 prepare_request() 方法。

3.3 get_adapter()(适配器选择)
  • 默认映射

    self.mount('http://', HTTPAdapter())
    self.mount('https://', HTTPAdapter())
  • 扩展点:可通过 session.mount() 注册自定义适配器。

4. 请求生命周期钩子(Hooks)

requests 支持在请求处理过程中插入钩子函数:

def log_response(resp, *args, **kwargs):
    print(f"Response: {resp.status_code}")

session = Session()
session.hooks['response'] = [log_response]
session.get('https://httpbin.org/get')
  • 支持的钩子类型

    • request: 请求发出前

    • response: 收到响应后

5. 超时与重试机制

5.1 超时控制
  • 参数格式

    timeout=5          # 连接+读取总超时
    timeout=(3, 5)     # 连接超时 3s,读取超时 5s
  • 底层实现:通过 urllib3 的 timeout 参数传递。

5.2 自动重试

需自定义适配器实现:

from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

retry_strategy = Retry(
    total=3, backoff_factor=1, status_forcelist=[500, 502]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("https://", adapter)

6. 总结:request() 方法执行流程图

步骤对应代码/模块关键操作
合并配置merge_setting()合并会话级配置(如 self.headers)和本次请求参数
构造Request对象models.Request封装 method/url/headers/data 等原始信息
预处理请求prepare_request()编码 URL 参数、序列化 JSON、生成 multipart 文件体
选择适配器get_adapter()根据 URL 协议选择 HTTP/HTTPS 适配器(默认 urllib3
发送请求adapter.send()通过连接池发送请求,处理超时和代理
处理重定向resolve_redirects()自动跟进 30x 跳转(若 allow_redirects=True
构建Responsebuild_response()将 urllib3 响应转换为 requests.Response 对象
返回结果-返回最终响应(可能经过多次重定向)

通过逐层分析,我们可以看到 requests 如何通过 清晰的模块划分 和 灵活的配置合并 实现简洁而强大的 HTTP 客户端功能。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐