Requests请求拦截:修改请求头与体内容

Requests请求拦截:修改请求头与体内容

【免费下载链接】requests 【免费下载链接】requests 项目地址: https://gitcode.com/gh_mirrors/req/requests

在网络请求开发中,我们经常需要在发送请求前对请求头(Header)和请求体(Body)进行修改,以满足特定的业务需求,如添加认证信息、修改数据格式等。Requests库(Python HTTP客户端库)提供了灵活的钩子(Hook)机制,允许开发者在请求生命周期的不同阶段介入并修改请求内容。本文将详细介绍如何利用Requests的钩子系统实现请求拦截,重点讲解修改请求头与请求体的具体方法,并通过实例展示其在实际开发中的应用。

一、Requests钩子系统概述

Requests库的钩子系统是实现请求拦截的核心机制,它允许开发者在请求的不同阶段注册回调函数,从而对请求或响应进行自定义处理。钩子系统基于事件驱动,当特定事件发生时(如请求发送前、响应接收后等),注册的回调函数会被自动触发。

1.1 钩子类型

目前Requests库主要支持response类型的钩子,即当响应对象(Response)被创建后触发。虽然没有直接的request钩子,但我们可以通过间接方式在请求发送前对其进行修改,这将在后续章节详细介绍。

1.2 钩子注册方式

钩子可以通过两种方式进行注册:

  1. 全局注册:通过Session对象的hooks属性注册,对该Session对象发送的所有请求生效。
  2. 局部注册:在每次发送请求时,通过requests.get()requests.post()等方法的hooks参数注册,仅对当前请求生效。

相关的源代码定义可以在src/requests/hooks.py中查看,其中default_hooks()函数初始化了默认的钩子字典,dispatch_hook()函数负责钩子的调度执行。

二、请求拦截原理

要实现请求拦截并修改请求头与请求体,关键在于理解Requests库发送请求的内部流程。当我们调用Session.request()方法发送请求时,大致会经历以下步骤:

  1. 创建Request对象。
  2. 调用Session.prepare_request()方法将Request对象转换为PreparedRequest对象(预准备请求对象),此时请求头和请求体已初步构建完成。
  3. 调用Session.send()方法发送PreparedRequest对象。

我们可以在PreparedRequest对象构建完成后、发送前对其进行修改。虽然Requests库没有直接提供request钩子,但我们可以利用Session对象的prepare_request()方法或自定义适配器(Adapter)来实现这一目的。

请求拦截流程

三、修改请求头

请求头包含了请求的元数据,如User-AgentContent-TypeAuthorization等。修改请求头是常见的需求,例如模拟不同的浏览器发送请求、添加认证令牌等。

3.1 通过Session.headers修改

Session对象的headers属性是一个字典,包含了默认的请求头。我们可以直接修改该字典来设置全局的请求头,对该Session对象发送的所有请求生效。

import requests

session = requests.Session()
# 设置全局请求头
session.headers.update({
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
    'Custom-Header': 'Custom-Value'
})

response = session.get('https://httpbin.org/get')
print(response.json()['headers'])

3.2 通过请求方法的headers参数修改

在发送单个请求时,可以通过headers参数设置或覆盖请求头,该参数仅对当前请求生效。

import requests

headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15',
    'Another-Header': 'Another-Value'
}

response = requests.get('https://httpbin.org/get', headers=headers)
print(response.json()['headers'])

3.3 通过钩子间接修改

虽然没有直接的request钩子,但我们可以利用response钩子的request属性(即生成该响应的请求对象)来间接修改请求头。不过需要注意的是,这种方式修改的是已经发送的请求的请求头,对当前请求的响应没有影响,但可以用于记录或调试。

import requests

def modify_request_header(response, **kwargs):
    # 获取生成该响应的请求对象
    request = response.request
    # 修改请求头(仅作演示,对当前响应无影响)
    request.headers['Modified-Header'] = 'Modified-Value'
    print('Modified request headers:', request.headers)
    return response

response = requests.get('https://httpbin.org/get', hooks={'response': modify_request_header})

四、修改请求体

请求体包含了需要发送给服务器的数据,通常在POST、PUT等请求中使用。修改请求体可以用于加密数据、格式化数据等场景。

4.1 通过PreparedRequest对象修改

PreparedRequest对象包含了最终要发送的请求数据,我们可以手动创建PreparedRequest对象,修改其body属性后再发送。

from requests import Request, Session

session = Session()
url = 'https://httpbin.org/post'
data = {'original': 'data'}

# 创建Request对象
req = Request('POST', url, data=data)
# 准备请求,得到PreparedRequest对象
prepped = session.prepare_request(req)

# 修改请求体
prepped.body = '{"modified": "data"}'
# 修改Content-Type请求头,确保服务器能正确解析修改后的请求体
prepped.headers['Content-Type'] = 'application/json'

# 发送修改后的请求
response = session.send(prepped)
print(response.json())

4.2 通过自定义Session修改

我们可以通过继承Session类,并重写其prepare_request()方法,在请求准备阶段统一修改请求体。

from requests import Session, Request

class CustomSession(Session):
    def prepare_request(self, request):
        prepped = super().prepare_request(request)
        # 在这里修改请求体
        if prepped.method in ['POST', 'PUT', 'PATCH'] and prepped.body:
            # 假设原始数据是表单格式,我们将其修改为JSON格式
            original_data = prepped.body.decode('utf-8')
            modified_data = f'{{"modified": "{original_data}"}}'
            prepped.body = modified_data.encode('utf-8')
            prepped.headers['Content-Type'] = 'application/json'
        return prepped

session = CustomSession()
response = session.post('https://httpbin.org/post', data={'key': 'value'})
print(response.json())

相关的Session类定义可以在src/requests/sessions.py中查看,其中prepare_request()方法负责将Request对象转换为PreparedRequest对象。

五、高级应用:请求签名

在实际开发中,我们可能需要对请求进行签名,以确保请求的合法性和完整性。请求签名通常需要将请求参数、时间戳等信息按照一定规则组合并加密,然后将加密结果添加到请求头或请求体中。

以下是一个请求签名的示例,通过自定义Session类,在请求准备阶段自动生成签名并添加到请求头:

import time
import hashlib
from requests import Session, Request

class SignedSession(Session):
    def __init__(self, api_key, api_secret):
        super().__init__()
        self.api_key = api_key
        self.api_secret = api_secret

    def prepare_request(self, request):
        prepped = super().prepare_request(request)
        
        # 生成时间戳
        timestamp = str(int(time.time()))
        # 获取请求参数(包括查询参数和请求体参数)
        params = dict(prepped.params)
        if prepped.body:
            # 假设请求体是表单格式,解析为字典
            body_params = dict(qc.split('=') for qc in prepped.body.decode('utf-8').split('&'))
            params.update(body_params)
        # 添加时间戳参数
        params['timestamp'] = timestamp
        # 按照键名排序
        sorted_params = sorted(params.items(), key=lambda x: x[0])
        # 拼接参数字符串
        param_str = '&'.join([f'{k}={v}' for k, v in sorted_params])
        # 生成签名(使用API密钥加密)
        signature = hashlib.md5(f'{param_str}{self.api_secret}'.encode('utf-8')).hexdigest()
        
        # 将API密钥和签名添加到请求头
        prepped.headers['X-API-Key'] = self.api_key
        prepped.headers['X-Signature'] = signature
        
        return prepped

# 使用示例
session = SignedSession(api_key='your_api_key', api_secret='your_api_secret')
response = session.post('https://httpbin.org/post', data={'param1': 'value1', 'param2': 'value2'})
print(response.json())

六、总结与展望

本文详细介绍了如何利用Requests库的钩子系统和PreparedRequest对象实现请求拦截,重点讲解了修改请求头和请求体的多种方法,并通过实例展示了其应用。通过灵活运用这些方法,我们可以满足各种复杂的业务需求,提高请求处理的灵活性和安全性。

未来,随着Requests库的不断发展,可能会提供更直接的请求钩子支持,使得请求拦截更加便捷。开发者也可以进一步探索自定义适配器(Adapter)等高级功能,实现更深度的请求定制。

官方文档中关于高级用法的更多内容,可以参考docs/user/advanced.rst。社区中也有许多关于Requests请求拦截的实践经验,例如README.md中提到的一些使用技巧和最佳实践。

【免费下载链接】requests 【免费下载链接】requests 项目地址: https://gitcode.com/gh_mirrors/req/requests

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值