文章目录
写在前面
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 # 作者用来将AuthObject的实例和固定的url作为元组保存起来,为后面_detect_auth(url, auth)方法使用 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 ################## 这几个放在一起的使用过程应该是这样子的 ################## # 1.注册用户信息 auth = requests.AuthObject('kennethreitz', 'xxxxxxx') # 2.将用户信息和url绑定,放到全局变量中,自动化进行认证的过程 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:
# url encode GET params if it's a dict
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)