Requests库请求过程简析

本文深入剖析了Python Requests库的请求过程,从API接口和Session出发,详细讲解了Request对象、prepare_request方法中的cookie处理、身份验证以及PreparedRequest对象的构造。文章通过源码分析,展示了请求发送的各个步骤,包括Request对象的创建、参数合并以及最终的网络请求,帮助读者更好地理解Requests库的工作机制。
摘要由CSDN通过智能技术生成

目录

API接口和Session

API接口

session

小结

请求流程介绍

Request对象

Request对象小结

prepare_request方法

cookie处理

cookie处理小结

身份验证

 构造PreparedRequest对象

prepare_request方法小结

环境参数合并

请求发送 

附源码结构梳理 


Requests是python中使用率极高的一个请求库,具体使用方法可以查询官网.

本文旨在从源码入手,梳理逻辑,以更好的理解Requsets库的请求过程.

API接口和Session

Requests库发起网络请求有两种方式,最简单的一种是API接口方式,一种是Session方式.

API接口是在方法层面,Session方式是在会话层面.

通过官方的介绍,我们知道方法层面的每次请求都是独立的,所有的请求的参数都是不持续的.

而会话层面的方法是可以将一些请求参数持续化的.比如cookie.

  • API接口

通过API接口方法的调用,就能得到响应内容,接下来用代码来看看API接口的方式

import requests
 
r = requests.get('https://api.github.com/events')
r = requests.post('https://httpbin.org/post', data = {'key':'value'})
r = requests.put('https://httpbin.org/put', data = {'key':'value'})
r = requests.delete('https://httpbin.org/delete')
r = requests.head('https://httpbin.org/get')
r = requests.options('https://httpbin.org/get')

上面见到的get,post等等方法都是在Reqeusts库的__init__文件提前导入的,很明显这些方法都来自api.py的文件.

from .api import request, get, head, post, patch, put, delete, options

接下来看看这些方法在api.py文件中是怎么定义的呢?(仅为展现需要,代码进行了删减)

from . import sessions

def request(method, url, **kwargs):
    pass

def get(url, params=None, **kwargs):
    pass

def post(url, data=None, json=None, **kwargs):
    pass

......

该api.py文件中一共定义了八个方法

除了第一个reqeust方法,其他的都是HTTP协议中的请求方法

暂时先不管这个reqeust方法,我们就从最常用的get方法入手,源码很简短,如下:

def get(url, params=None, **kwargs):
    kwargs.setdefault('allow_redirects', True)
    return request('get', url, params=params, **kwargs)

 要搞懂这段代码,首先要了解**kwargs形式的参数.简单来说,这是python中一种对参数的封装方式.

 可以把 key=value 这样键值对形式的参数封装成字典,举个例子:

def func(**kwargs):
    print(kwargs)
func(a=1,b=2)
#打印:{'a': 1, 'b': 2}

了解了kwargs就是一个字典后,就能很容易的看懂第一行代码了.

通过字典的内置setdefault方法,为kwargs这个参数字典设置一个allow_redirects主键,如果该主键不存在则值为True.

实际上allow_redirects是代表允许重定向

最后通过调用之前提到的reqeust方法,并将request方法的结果返回.

至此,可以来研究下reqeust方法的源码了:

from . import sessions

def request(method, url, **kwargs):
    with sessions.Session() as session:
        return session.request(method=method, url=url, **kwargs)

这个request方法的源代码也极为简洁 ,但是注意该方法调用了session模块的内容.

通过上下文管理的方式调用了session.py文件中的Session类的request方法.

如果熟悉with关键字的作用的话,便理解为什么方法层面的请求中的参数是不可持续的了.

虽然现在还不清楚session.Session.request方法中的逻辑

但起码知道每一次方法层面的请求都是在调用会话层的方法,并在一次请求结束后关闭了session.

在此就仅以get方法为例了,其他的post,put等等方法也都是类似的.

  • session

由上述所知,方法层面都是调用的session.py文件中的方法.

那么现在来谈谈会话层了.首先来看看session.py文件中的内容.

# import so much things!!!

def merge_setting(request_setting, session_setting, dict_class=OrderedDict):
    pass

def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict):
    pass

class SessionRedirectMixin(object):
    pass

class Session(SessionRedirectMixin):
    pass

def session():
    pass

该文件中导入的其他模块或者方法很多,等碰到有用的模块或方法时在介绍,此处就不啰嗦了.

在该文件中定义了3个方法和2个类

既然之前被调用的是Session类,那么就从Session类先看.

在研究之前要声明一点,本文只探讨Request库的请求过程,至于网络传输等等其他方面的不在本文研究范围内.

下面我们先列出Session类中的在本次讨论范围内的部分代码(同样做了删减)

class Session(SessionRedirectMixin):

    def prepare_request(self, request):
        pass
    
    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):
        pass
    def get(self, url, **kwargs):
        kwargs.setdefault('allow_redirects', True)
        return self.request('GET', url, **kwargs)

    def send(self, request, **kwargs):
        pass

既然刚刚是调用的reqeust方法,那么就直接研究Session类中的这个reqeust方法吧.

咋眼一看,该方法的参数实在不少...... 还是直接上源码吧

    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):
        
        req = Request(
            method=method.upper(),
            url=url,
            headers=headers,
            files=files,
            data=data or {},
            json=json,
            params=params or {},
            auth=auth,
            cookies=cookies,
            hooks=hooks,
        )
        prep = self.prepare_request(req)

        proxies = proxies or {}

        settings = self.merge_environment_settings(
            prep.url, proxies, stream, verify, cert
        )

        send_kwargs = {
            'timeout': timeout,
            'allow_redirects': allow_redirects,
        }
        send_kwargs.update(settings)
        resp = self.send(prep, **send_kwargs)

        return resp

不管怎么样,看到最后的return时,起码知道这个request方法才是最后拿到响应内容的核心方法了.

其实这一堆的参数都和HTTP协议是相关了,也就是在请求体中要发给Sever端的相关的必要的参数和内容.

先大概看看这个reqeust方法干了些什么事吧,把最关键的几个步骤梳理下.

首先实例化了一个Request对象req(request这个词用的比较多,注意区分)

然后调用 self.prepare_request 方法处理这个实例对象req,并生成一个新的prep对象

接下来进行了一些参数合并之类的工作,也就是把方法层面请求的参数与会话层面请求的参数进行合并

最后通过self.send方法将构造好的prep对象发送出去,并得到响应内容对象resp.

  • 小结

通过之前的分析,我们知道了以下几点:

1、api.py文件中定义的方法(如get)是通过request方法封装后调用的session

2、session.py文件中的Session类的request方法才是拿到网络响应的核心方法

3、上下文管理with的方式导致了每次API方法请求的参数的不可持续性

4、在向网络发送请求前,API方法携带的参数与session的参数进行了合并

请求流程介绍

前面的流程梳理中,我们已经知道Session类的request方法是整个请求调用的核心方法

同时也梳理出了整个流程的步骤

先构造Request对象req

然后使用self.prepare_request 方法处理这个实例对象req,并生成一个新的prep对象

接着合并环境参数

最后发送prep对象和环境参数并得到网络响应

接下来就按照这个步骤对整个流程进行分析

Request对象

在HTTP协议中,Client端、Sever端的使用Request(请求)和Response(响应)两个对象进行交互.

要进行网络交互,必然就存在着这两个对象

而之前的已经介绍到了session.Session.request方法,该方法中的第一步也是实例化了一个Request对象req.

其实这个Request类位于Requests库中的models.py文件中

在models.py文件中,声明了五个类, 分别是:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值