美文网首页
requests源码简略阅读

requests源码简略阅读

作者: 点点寒彬 | 来源:发表于2019-06-20 00:12 被阅读0次

背景

requests库号称是为人类设计的HTTP请求库,个人也经常使用这个库,因此对它的源码产生了兴趣,带着这个目的去看看它的源码。

api

requests的最外层是api层,这里提供了对外暴露的接口,比如我们使用requests.get那么这个get方法就是在api这里定义的方法。

def get(url, params=None, **kwargs):
    """Sends a GET request.

    :param url: URL for the new :class:`Request` object.
    :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
    :param \*\*kwargs: Optional arguments that ``request`` takes.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response
    """

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

这里观察了一系列的方法啊,可以看到这里其实调用的是api中的request方法,而request中调用的是session中的方法。继续跟到session中去

session

sessionrequests用来会话管理的这么一个类,在api层看到的方法参数其实只是一个**kwargs,我们并不知道可以传什么参数进来,但是在session层我们就能看到它接受的参数了。

    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):

api层会根据调用方法定义好method,而url是用户传入的参数,我们常用的paramsdataheaderscookies等参数在这里就能看到了。

之后代码会把这些参数用来初始化Model层的一个Request对象。初始化后调用prepare_request方法做请求前的准备,其实说白了就是要做请求串的拼装了,因为HTTP请求,本质上来说是TCP的请求,只是数据格式被严格定义好了的字符串。

prepare

准备阶段首先是处理Cookie

cookies = request.cookies or {}

# Bootstrap CookieJar.
if not isinstance(cookies, cookielib.CookieJar):
    cookies = cookiejar_from_dict(cookies)

# Merge with session cookies
merged_cookies = merge_cookies(
    merge_cookies(RequestsCookieJar(), self.cookies), cookies)

从源码可以看到,Cookie是通过cookiejar这个对象来管理的,这里会把传入的cookie与会话中的cookie做一个合并。再往下就是鉴权。

cookie和鉴权处理了之后,就会把参数全部用来初始化Model层的PreparedRequest对象,之后把这个对象返回回去。

当然,在PreparedRequest对象中会把改解析的数据全部做一次解析,源码如下:

    def prepare(self, method=None, url=None, headers=None, files=None,
        data=None, params=None, auth=None, cookies=None, hooks=None, json=None):
        """Prepares the entire request with the given parameters."""

        self.prepare_method(method)
        self.prepare_url(url, params)
        self.prepare_headers(headers)
        self.prepare_cookies(cookies)
        self.prepare_body(data, files, json)
        self.prepare_auth(auth, url)

        # Note that prepare_auth must be last to enable authentication schemes
        # such as OAuth to work on a fully prepared request.

        # This MUST go after prepare_auth. Authenticators could add a hook
        self.prepare_hooks(hooks)

这里继续跟下去,会看到很多编码的处理。具体的代码有兴趣的可以自己跟一下,基本上都是一些字符串的处理。

  • Cookie的处理

值得一提的是,之前我一直有一个困惑,Cookie在请求头里面的,为什么requests的参数中会把cookie单独列出来,我直接在headers里面写上"Cookie": "abcdefg"和用cookie参数传进去会有什么区别。从源代码里面去看,实际上是没有区别的。

    def prepare_cookies(self, cookies):
        if isinstance(cookies, cookielib.CookieJar):
            self._cookies = cookies
        else:
            self._cookies = cookiejar_from_dict(cookies)

        cookie_header = get_cookie_header(self._cookies, self)
        if cookie_header is not None:
            self.headers['Cookie'] = cookie_header

可以看到,最后会把cookiejar对象中的数据转成字符串后扔到header里面。其实也确实应该最终放到头部,毕竟在HTTP标准中,Cookie就是放在头部往下传的。

  • 数据层的处理

请求数据有三种类型,json数据、表单数据文件

首先会处理json

if not data and json is not None:
    content_type = 'application/json'
    body = complexjson.dumps(json)

如果传入参数是json,那么就会把请求的Content-Type设置为application/json,然后用json.dumps()去处理数据。

然后来判断是否是流信息。

is_stream = all([
            hasattr(data, '__iter__'),
            not isinstance(data, (basestring, list, tuple, dict))
        ])

这里有意思的是all这个方法。

再接下来是处理文件类型和如果是文件类型。

文件类型支持多个参数,比较重要的是要调用open方法把文件打开后传入,而源码在处理的时候会调用对象的read()方法,fdata = fp.read()

最后才是处理表单类型的数据

if data:
    body = self._encode_params(data)
    if isinstance(data, basestring) or hasattr(data, 'read'):
        content_type = None
    else:
        content_type = 'application/x-www-form-urlencoded'

发送请求

发送请求其实调用的是urllib3的能力。在上面的准备阶段做完之后,会把配置做一个合并,然后调用send方法,send方法会调到adapter.py中封装的方法,请求完毕后,返回请求的结果。为了使用起来友好,在拿到返回结果之后,会把请求的对象和返回的对象做一个合并。

return self.build_response(request, resp)

    def build_response(self, req, resp):
        response = Response()

        # Fallback to None if there's no status_code, for whatever reason.
        response.status_code = getattr(resp, 'status', None)

        # Make headers case-insensitive.
        response.headers = CaseInsensitiveDict(getattr(resp, 'headers', {}))

        # Set encoding.
        response.encoding = get_encoding_from_headers(response.headers)
        response.raw = resp
        response.reason = response.raw.reason

        if isinstance(req.url, bytes):
            response.url = req.url.decode('utf-8')
        else:
            response.url = req.url

        # Add new cookies from the server.
        extract_cookies_to_jar(response.cookies, req, resp)

        # Give the Response some context.
        response.request = req
        response.connection = self

        return response

这里就是对返回结果的一些封装了。

最后

可以看到requests库的封装比较好,通过api层来暴露接口,往下用一个session来做规划管理,收拢数据和配置,再根据关键的method做路由分发,组参数。最后调用请求方法,通过这样的分层包装,使用起来非常简单,但功能却很强大。

相关文章

  • requests源码简略阅读

    背景 requests库号称是为人类设计的HTTP请求库,个人也经常使用这个库,因此对它的源码产生了兴趣,带着这个...

  • Requests源码阅读

    库结构 整体结构 这是我第一次看源码,版本为requests-2.18.4.说实话很吃力,有这几个原因 英文水平不...

  • Requests源码阅读

    很久之前阅读的,差点忘了 一、_Requests模块的作用 刚开始看时,不太注意注释里面的英文,然后就看它作用是继...

  • requests 库源码阅读

    Python 通过调用 warnings 模块中定义的 warn() 函数来发出警告。警告消息通常用于提示用户一些...

  • ArrayMap,SparseArray,HashMap特性VS

    本篇是入门教程,旨在短小精悍,简略介绍概要设计,面对源码时也知如何下手,深入理解细节请阅读源码。 HashMap ...

  • 接口测试——requests 的基本了解

    ● requests介绍及安装 ● requests原理及源码介绍 ● 使用requests发送请求 ● 使用re...

  • python requests库源码阅读

    python requests库提供了丰富便捷的http接口,属于python中最常用的第三方库之一。之前用req...

  • 如何阅读源代码(以 Python requests 库为例)

    如何阅读源代码(以 Python requests 库为例) 对任何一位刚开始阅读源码的工程师来说,学会循序渐进,...

  • AsyncTask源码简略分析

    要查看一个类实现的流程,我们需要先查看它类说明和成员变量。 AsyncTask说明 大概意思就是说AsyncTas...

  • requests源码分析(1)

    目录结构 Requests-2.18.4的源码目录结构如下所示: 其中核心部分无疑是requests目录了。 代码...

网友评论

      本文标题:requests源码简略阅读

      本文链接:https://www.haomeiwen.com/subject/swrrqctx.html