文章目录
写在前面拉取版本源码结构目录如下具体文件分析readme文件history文件core文件get函数get函数源码get函数解读
Request类send方法源码send方法解读__setattr__方法解读
_Request类
写在前面
requests库是公认的python最好上手的http请求库,基于urllib库重新造轮子,用起来非常顺手。而且,这也是传说中最pythonic的代码,毕竟作者是公认的python界前三位的大神,先来看看初始版本(0.2.0版本)
拉取版本源码
# 有些人不知道如何拿到tag版本的源码,这里说一下哈,两种方法
# 第一种: 下载选定版本压缩包,解压即可
在github requests库找到tag=0.2.0的标签,下载压缩包解压到文件夹即可
# 第二种: git 操作
git clone https://github.com/psf/requests.git
# 切换到 0.2.0版本
git checkout v0.2.0.
# 进入项目目录即可
cd requests
结构目录如下
requests
-docs
-HISTORY.rst
-LICENSE
-README.rst
-requests
- __init__.py
- core.py
-setup.py
-test_requests.py
具体文件分析
readme文件
截取了一部分原readme,总的来说,就是大神当时觉得已存在的python语言的http库都极其难用,所以想重新造轮子,并在此文件中体现了自己对于库设计的理念以及如何非常容易上手这个项目的使用(介绍了如何进行http/https请求的例子)对于一个项目来说,readme是所有人想了解你项目的第一步,大神的readme写的确实非常“嚣张”,但确实够吸引人,也确实好用,不然怎么叫大神呢(所以如果自己的项目,一定要认真对待readme文件,readme让别人感兴趣,别人才可能研究并使用你的项目)
Requests: The Simple (e.g. usable) HTTP Module
==============================================
Most existing Python modules for dealing HTTP requests are insane. I have to look up *everything* that I want to do. Most of my worst Python experiences are a result of the various built-in HTTP libraries (yes, even worse than Logging).
But this one's different. This one's going to be awesome. And simple.
Really simple.
Features
--------
- Extremely simple GET, HEAD, POST, PUT, DELETE Requests
+ Simple HTTP Header Request Attachment
+ Simple Data/Params Request Attachment
- Simple Basic HTTP Authentication
+ Simple URL + HTTP Auth Registry
Usage
-----
It couldn't be simpler. ::
>>> import requests
>>> r = requests.get('http://google.com')
HTTPS? Basic Authentication? ::
>>> r = requests.get('https://convore.com/api/account/verify.json')
>>> r.status_code
401
Uh oh, we're not authorized! Let's add authentication. ::
>>> conv_auth = requests.AuthObject('requeststest', 'requeststest')
>>> r = requests.get('https://convore.com/api/account/verify.json', auth=conv_auth)
>>> r.status_code
200
>>> r.headers['content-type']
'application/json'
history文件
只传达出了一个意思,requests正式版本由0.2.0开始,时间为2011-02-14
History
-------
0.2.0 (2011-02-14)
++++++++++++++++++
* Birth!
0.0.1 (2011-02-13)
++++++++++++++++++
* Frustration
* Conception
core文件
通过分析,可以看得出来,核心代码文件为core.py,行数不多,只有400行左右的样子,我们从调用过程来看
get函数
get函数源码
get函数是最基础的,先研究下它
def get(url
, params
={}, headers
={}, auth
=None):
r
= Request
()
r
.method
= 'GET'
r
.url
= url
r
.params
= params
r
.headers
= headers
r
.auth
= _detect_auth
(url
, auth
)
r
.send
()
return r
.response
get函数解读
首先实例化,生成一个Request对象,然后将初始化方法的属性重新设置,只有一个例外,就是auth
_detect_autu方法说白了,因为有些网站需要用户名和密码的认证,才能访问,所以这里就是为了调用urllib2中用来处理这种情况的handler,下面我们来看一下代码(以下这几个函数/类需要放在一起看)
class AuthObject(object):
def __init__(self
, username
, password
):
self
.username
= username
self
.password
= password
def add_autoauth(url
, authobject
):
"""
global AUTOAUTHS
AUTOAUTHS.append((url, authobject))
# 如果没传递AuthObject实例对象进来,则需要在全局变量中AUTOAUTHS查看url是否存在对应的auth认证
def _detect_auth(url, auth):
"""Returns registered AuthObject
for given url
if available
, defaulting to
given AuthObject
."""
return _get_autoauth(url) if not auth else auth
# 查看url是否需要调用urllib2中专门用来处理需要用户名密码的handler,如果需要,则返回实例化的 AuthObject(用户名,密码),否则直接返回None,用默认urllib2中默认的default_handlers即可
def _get_autoauth(url):
"""Returns registered AuthObject
for given url
if available
.
"""
for (autoauth_url
, auth
) in AUTOAUTHS
:
if autoauth_url
in url
:
return auth
return None
auth
= requests
.AuthObject
('kennethreitz', 'xxxxxxx')
requests
.add_autoauth
('https://convore.com/api/', c_auth
)
3.这个get并没有传递auth信息,但是会从全局变量中检测到这个domain存在authobject实例对象的存在
r
= requests
.get
('https://convore.com/api/account/verify.json')
4.send函数则最终会调用HTTPBasicAuthHandler处理请求
4.最后一步,调用send方法
Request类
send方法源码
def send(self
, anyway
=False):
"""Sends the request. Returns True of successfull, false if not.
If there was an HTTPError during transmission,
self.response.status_code will contain the HTTPError code.
Once a request is successfully sent, `sent` will equal True.
:param anyway: If True, request will be sent, even if it has
already been sent.
"""
self
._checks
()
success
= False
if self
.method
in ('GET', 'HEAD', 'DELETE'):
if (not self
.sent
) or anyway
:
if isinstance(self
.params
, dict):
params
= urllib
.urlencode
(self
.params
)
else:
params
= self
.params
req
= _Request
(("%s?%s" % (self
.url
, params
)), method
=self
.method
)
if self
.headers
:
req
.headers
= self
.headers
opener
= self
._get_opener
()
try:
resp
= opener
(req
)
self
.response
.status_code
= resp
.code
self
.response
.headers
= resp
.info
().dict
if self
.method
.lower
() == 'get':
self
.response
.content
= resp
.read
()
success
= True
except urllib2
.HTTPError
, why
:
self
.response
.status_code
= why
.code
send方法解读
self._checks(),先检验url是否有问题,很简单,不说了
if (not self.sent) or anyway :我是这样理解的,假设说我们用Request实例对象去请求相同的请求,那么第一次成功的话,就会将self.sent设置为True,那么接下来的重复请求就不会发起了,除非将anyway设置为True,那么才会再此发起相同的请求,反正第一次请求是绝对会正常执行的
urllib.urlencode(self.params) 就是将请求参数编码
_Request(("%s?%s" % (self.url, params)), method=self.method)是将域名 + 请求拼接完整,这样服务器才知道客户端的请求内容
_get_opener就是找让哪些handler来处理相关的url请求,这里作者考虑的是如果需要基础验证,则创建一个HTTPBasicAuthHandler的实例,并让opener使用这个handler ,否则就用urllib2.urlopen函数即可。(urlopen其实是opener的一个实例,内部也还是调用了build_opener函数去拿到handlers,在urllib2的原生代码,想要理解,还需要看下urllib2的源码,其实注释的比较清晰了)
def _get_opener(self):
""" Creates appropriate opener object for urllib2.
"""
if self.auth:
# create a password manager
authr = urllib2.HTTPPasswordMgrWithDefaultRealm()
authr.add_password(None, self.url, self.auth.username, self.auth.password)
handler = urllib2.HTTPBasicAuthHandler(authr)
opener = urllib2.build_opener(handler)
# use the opener to fetch a URL
return opener.open
else:
return urllib2.urlopen
接下来就比较简单了,将_Request实例对象发送给服务器,如果是没有鉴权,那就相当于resp = urllib2.urlopen(urllib2.Reuqest())了
最后的最后则是通过属性接收响应结果
__setattr__方法解读
这个__setattr__就是对url请求方法做了一个限制,只支持类属性_METHODS中存在的请求方法
class Request(object):
"""The :class:`Request` object. It carries out all functionality of
Requests. Recommended interface is with the Requests functions.
"""
_METHODS
= ('GET', 'HEAD', 'PUT', 'POST', 'DELETE')
def __init__(self
):
self
.url
= None
self
.headers
= dict()
self
.method
= None
self
.params
= {}
self
.data
= {}
self
.response
= Response
()
self
.auth
= None
self
.sent
= False
def __setattr__(self
, name
, value
):
if (name
== 'method') and (value
):
if not value
in self
._METHODS
:
raise InvalidMethod
()
object.__setattr__
(self
, name
, value
)
_Request类
这个类就是继承了urllib2.Requests的类,没什么好说的
class _Request(urllib2
.Request
):
"""Hidden wrapper around the urllib2.Request object. Allows for manual
setting of HTTP methods.
"""
def __init__(self
, url
,
data
=None, headers
={}, origin_req_host
=None,
unverifiable
=False, method
=None):
urllib2
.Request
.__init__
( self
, url
, data
, headers
, origin_req_host
,
unverifiable
)
self
.method
= method
def get_method(self
):
if self
.method
:
return self
.method
return urllib2
.Request
.get_method
(self
)