目录
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文件中,声明了五个类, 分别是: