Python 标准库中的 urllib2 模块提供了你所需要的大多数 HTTP 功能,但是它的 API 太渣了。它是为另一个时代、另一个互联网所创建的。它需要巨量的工作,甚至包括各种方法覆盖,来完成最简单的任务。

Requests 完全满足如今网络的需求。

  • 国际化域名和 URLs
  • Keep-Alive & 连接池
  • 持久的 Cookie 会话
  • 类浏览器式的 SSL 加密认证
  • 基本/摘要式的身份认证
  • 优雅的键/值 Cookies
  • 自动解压
  • Unicode 编码的响应体
  • 多段文件上传
  • 连接超时
  • 支持 .netrc
  • 适用于 Python 2.6—3.4
  • 线程安全

会话对象:

  会话就是session,session的实现是基于cookie的,所以会话对象能够跨请求保持一些参数,也可以在同一个session实例发出的所有请求之间保持cookies。

import requests
s = requests.Session()
s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
r=s.get('http://httpbin.org/cookies')

会话对象也可以为其你去提供缺省数据,通过会话对象的属性提哦给你数据来实现的

s = requests.Session()
s.auth = ('user', 'pass')
s.headers.update({'x-test': 'true'}) # both 'x-test' and 'x-test2' are sent
s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})

会话对象传入的参数会自动覆盖初始化中的默认参数。

请求与响应对象

任何时候调用requests.*()你都在做两件主要的事情。其一,你在构建一个 Request 对象, 该对象将被发送到某个服务器请求或查询一些资源。其二,一旦 requests 得到一个从 服务器返回的响应就会产生一个 Response 对象。该响应对象包含服务器返回的所有信息, 也包含你原来创建的 Request 对象。

r = requests.get('http://www.baidu.com')
r.headers

获取响应头的信息。查看http协议了解响应头的内容

下面的代码是获取请求的内容

r.request.headers

Prepared Requests

当你从一个api请求或者一个session请求接受到一个响应对象的时候,请求的参数实际上是PreparedRequest 对象使用的,如果你想在提交请求之前在请求体body或者请求头header做一些操作的时候。

from requests import Request, Session

s = Session()
req = Request('GET', url,
data=data,
headers=header
)
prepped = req.prepare() # do something with prepped.body
# do something with prepped.headers resp = s.send(prepped,
stream=stream,
verify=verify,
proxies=proxies,
cert=cert,
timeout=timeout
) print(resp.status_code)

可以理解。Requests对象其实就是当PreparedRequest 没有被修改时直接提交的。最后的请求方式是 requests.* 或者 Session.*.

上面的代码在使用Requests的session对象时很可能会丢失一些优势,session级别的状态,比如说cookie就不会在request请求中,用Session.prepare_request()代替Request.prepare() 就可以完美的实现session级别的状态。

from requests import Request, Session

s = Session()
req = Request('GET', url,
data=data
headers=headers
) prepped = s.prepare_request(req) # do something with prepped.body
# do something with prepped.headers resp = s.send(prepped,
stream=stream,
verify=verify,
proxies=proxies,
cert=cert,
timeout=timeout
) print(resp.status_code)

SSL证书验证

SSL证书通过在客户端浏览器Web服务器之间建立一条SSL安全通道(Secure socket layer(SSL)安全协议是由Netscape Communication公司设计开发。该安全协议主要用来提供对用户和服务器的认证;对传送的数据进行加密和隐藏;确保数据在传送中不被改变,即数据的完整性,现已成为该领域中全球化的标准。由于SSL技术已建立到所有主要的浏览器WEB服务器程序中,因此,仅需安装服务器证书就可以激活该功能了)。即通过它可以激活SSL协议,实现数据信息在客户端和服务器之间的加密传输,可以防止数据信息的泄露。保证了双方传递信息的安全性,而且用户可以通过服务器证书验证他所访问的网站是否是真实可靠。

Requests可以为HTTPS请求验证SSL证书,就像web浏览器一样。要想检查某个主机的SSL证书,你可以使用 verify 参数:

requests.get('https://kennethreitz.com', verify=True)

在该域名上我没有设置SSL,所以失败了。但Github设置了SSL:

requests.get('https://github.com', verify=True)

如果你将 verify 设置为False,Requests也能忽略对SSL证书的验证

requests.get('https://kennethreitz.com', verify=False)

默认情况下, verify 是设置为True的。选项 verify 仅应用于主机证书。

你也可以指定一个本地证书用作客户端证书,可以是单个文件(包含密钥和证书)或一个包含两个文件路径的元组:

requests.get('https://kennethreitz.com', cert=('/path/server.crt', '/path/key'))

如果你指定了一个错误路径或一个无效的证书:

requests.get('https://kennethreitz.com', cert='/wrong_path/server.pem')
SSLError: [Errno 336265225] _ssl.c:347: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib

响应体内容工作流

默认情况下,当你进行网络请求后,响应体会立即被下载。你可以通过 stream 参数覆盖这个行为,推迟下载响应体直到访问 Response.content 属性:

tarball_url = 'https://github.com/kennethreitz/requests/tarball/master'
r = requests.get(tarball_url, stream=True)

此时仅有响应头被下载下来了,连接保持打开状态,因此允许我们根据条件获取内容:

if int(r.headers['content-length']) < TOO_LONG:
content = r.content
...

当讲一个request请求的stream=True时,连接connection不会被返回放入到连接池中去除非你读取完requests中所有的数据或者直接调用Requests.close()方法。这必然导致连接的效率很低,当只需要响应的正文部分(或者什么都不需要读取),可以试试contextlib.closing

from contextlib import closing

with closing(requests.get('http://httpbin.org/get', stream=True)) as r:
# Do things with the response here.

保持活动状态(持久连接)

好消息 - 归功于urllib3,同一会话内的持久连接是完全自动处理的!同一会话内你发出的任何请求都会自动复用恰当的连接!只有所有的响应体数据被读取完毕连接才会被释放为连接池;所以确保将 stream设置为 False 或读取 Response 对象的 content 属性。

流式上传

Requests支持流式上传,这允许你发送大的数据流或文件而无需先把它们读入内存。要使用流式上传,仅需为你的请求体提供一个类文件对象即可:

with open('massive-body') as f:
requests.post('http://some.url/streamed', data=f)

块编码请求

对于出去和进来的请求,Requests也支持分块传输编码。要发送一个块编码的请求,仅需为你的请求体提供一个生成器(或任意没有具体长度(without a length)的迭代器)

def gen():
yield 'hi'
yield 'there' requests.post('http://some.url/chunked', data=gen())

多文件同时上传

当需要在一次请求中上传多个文件的时候,如:

<input type=”file” name=”images” multiple=”true” required=”true”/>

这时候可以将文件设置成一个元组列表(文件名称,文件信息)。

url = 'http://httpbin.org/post'
multiple_files = [('images', ('foo.png', open('foo.png', 'rb'), 'image/png')),
('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))]
r = requests.post(url, files=multiple_files)
r.text

流式请求

使用 requests.Response.iter_lines() 你可以很方便地对流式API(例如 Twitter的流式API )进行迭代。简单地设置 stream 为 True 便可以使用 iter_lines() 对相应进行迭代:

import json
import requests r = requests.get('http://httpbin.org/stream/20', stream=True) for line in r.iter_lines(): # filter out keep-alive new lines
if line:
print(json.loads(line))

代理

如果需要使用代理,你可以通过为任意请求方法提供 proxies 参数来配置单个请求:

import requests

proxies = {
"http": "http://10.10.1.10:3128",
"https": "http://10.10.1.10:1080",
} requests.get("http://example.org", proxies=proxies)

你也可以通过环境变量 HTTP_PROXY 和 HTTPS_PROXY 来配置代理。

$ export HTTP_PROXY="http://10.10.1.10:3128"
$ export HTTPS_PROXY="http://10.10.1.10:1080"
$ python
import requests
requests.get("http://example.org")

若你的代理需要使用HTTP Basic Auth,可以使用 http://user:password@host/ 语法:

proxies = {
"http": "http://user:pass@10.10.1.10:3128/",
}

编码方式

当你收到一个响应时,Requests会猜测响应的编码方式,用于在你调用 Response.text方法时 对响应进行解码。Requests首先在HTTP头部检测是否存在指定的编码方式,如果不存在,则会使用 charade 来尝试猜测编码方式

只有当HTTP头部不存在明确指定的字符集,并且 Content-Type 头部字段包含 text 值之时, Requests才不去猜测编码方式

在这种情况下, RFC 2616 指定默认字符集 必须是 ISO-8859-1 。Requests遵从这一规范。如果你需要一种不同的编码方式,你可以手动设置 Response.encoding 属性,或使用原始的 Response.content

HTTP动词

Requests提供了几乎所有HTTP动词的功能:GET,OPTIONS, HEAD,POST,PUT,PATCH和DELETE。 以下内容为使用Requests中的这些动词以及Github API提供了详细示例。

我将从最常使用的动词GET开始。HTTP GET是一个幂等的方法,从给定的URL返回一个资源。因而, 当你试图从一个web位置获取数据之时,你应该使用这个动词。一个使用示例是尝试从Github上获取 关于一个特定commit的信息。假设我们想获取Requests的commit a050faf 的信息。我们可以 这样去做

import requests
r = requests.get('https://api.github.com/repos/kennethreitz/requests/git/commits/a050faf084662f3a352dd1a941f2c7c9f886d4ad')

我们应该确认Github是否正确响应。如果正确响应,我们想弄清响应内容是什么类型的。像这样去做:

if (r.status_code == requests.codes.ok):
... print r.headers['content-type']
...
application/json; charset=utf-8

可见,GitHub返回了JSON数据,非常好,这样就可以使用 r.json 方法把这个返回的数据解析成Python对象

>>> commit_data = r.json()
>>> print commit_data.keys()
[u'committer', u'author', u'url', u'tree', u'sha', u'parents', u'message']
>>> print commit_data[u'committer']
{u'date': u'2012-05-10T11:10:50-07:00', u'email': u'me@kennethreitz.com', u'name': u'Kenneth Reitz'}
>>> print commit_data[u'message']
makin' history

到目前为止,一切都非常简单。嗯,我们来研究一下GitHub的API。我们可以去看看文档, 但如果使用Requests来研究也许会更有意思一点。我们可以借助Requests的OPTIONS动词来看看我们刚使用过的url 支持哪些HTTP方法

>>> verbs = requests.options(r.url)
>>> verbs.status_code
500

额,这是怎么回事?毫无帮助嘛!原来GitHub,与许多API提供方一样,实际上并未实现OPTIONS方法。 这是一个恼人的疏忽,但没关系,那我们可以使用枯燥的文档。然而,如果GitHub正确实现了OPTIONS, 那么服务器应该在响应头中返回允许用户使用的HTTP方法,例如

>>> verbs = requests.options('http://a-good-website.com/api/cats')
>>> print verbs.headers['allow']
GET,HEAD,POST,OPTIONS

转而去查看文档,我们看到对于提交信息,另一个允许的方法是POST,它会创建一个新的提交。 由于我们正在使用Requests代码库,我们应尽可能避免对它发送笨拙的POST。作为替代,我们来 玩玩GitHub的Issue特性

>>> r = requests.get('https://api.github.com/repos/kennethreitz/requests/issues/482')
>>> r.status_code
200
>>> issue = json.loads(r.text)
>>> print issue[u'title']
Feature any http verb in docs
>>> print issue[u'comments']
3

使用https://api.github.com/repos/kennethreitz/requests/issues/482为例

r = requests.get('https://api.github.com/repos/kennethreitz/requests/issues/482')
>>> r.status_code
200
>>> issue = json.loads(r.text)
>>> print issue[u'title']
Feature any http verb in docs
>>> print issue[u'comments']

有3个评论。我们来看一下最后一个评论

>>> r = requests.get(r.url + u'/comments')
>>> r.status_code
200
>>> comments = r.json()
>>> print comments[0].keys()
[u'body', u'url', u'created_at', u'updated_at', u'user', u'id']
>>> print comments[2][u'body']
Probably in the "advanced" section

嗯,那看起来似乎是个愚蠢之处。我们发表个评论来告诉这个评论者他自己的愚蠢。那么,这个评论者是谁呢?

>>> print comments[2][u'user'][u'login']
kennethreitz

好,我们来告诉这个叫肯尼思的家伙,这个例子应该放在快速上手指南中。根据GitHub API文档, 其方法是POST到该话题。我们来试试看

>>> body = json.dumps({u"body": u"Sounds great! I'll get right on it!"})
>>> url = u"https://api.github.com/repos/kennethreitz/requests/issues/482/comments"
>>> r = requests.post(url=url, data=body)
>>> r.status_code
404

这有点古怪哈。可能我们需要验证身份。那就有点纠结了,对吧?不对。Requests简化了多种身份验证形式的使用, 包括非常常见的Basic Auth

>>> from requests.auth import HTTPBasicAuth
>>> auth = HTTPBasicAuth('fake@example.com', 'not_a_real_password')
>>> r = requests.post(url=url, data=body, auth=auth)
>>> r.status_code
201
>>> content = r.json()
>>> print(content[u'body'])
Sounds great! I'll get right on it.

精彩!噢,不!我原本是想说等我一会,因为我得去喂一下我的猫。如果我能够编辑这条评论那就好了! 幸运的是,GitHub允许我们使用另一个HTTP动词,PATCH,来编辑评论。我们来试试

>>> print(content[u"id"])
5804413
>>> body = json.dumps({u"body": u"Sounds great! I'll get right on it once I feed my cat."})
>>> url = u"https://api.github.com/repos/kennethreitz/requests/issues/comments/5804413"
>>> r = requests.patch(url=url, data=body, auth=auth)
>>> r.status_code
200

非常好。现在,我们来折磨一下这个叫肯尼思的家伙,我决定要让他急得团团转,也不告诉他是我在捣蛋。 这意味着我想删除这条评论。GitHub允许我们使用完全名副其实的DELETE方法来删除评论。我们来清除该评论。

>>> r = requests.delete(url=url, auth=auth)
>>> r.status_code
204
>>> r.headers['status']
'204 No Content'

很好。不见了。最后一件我想知道的事情是我已经使用了多少限额(ratelimit)。查查看,GitHub在响应头部发送这个信息, 因此不必下载整个网页,我将使用一个HEAD请求来获取响应头

>>> r = requests.head(url=url, auth=auth)
>>> print r.headers
...
'x-ratelimit-remaining': ''
'x-ratelimit-limit': ''
...

响应头链接字段

许多HTTP API都有响应头链接字段的特性,它们使得API能够更好地自我描述和自我显露。

GitHub在API中为 分页 使用这些特性,例如:

>>> url = 'https://api.github.com/users/kennethreitz/repos?page=1&per_page=10'
>>> r = requests.head(url=url)
>>> r.headers['link']
'<https://api.github.com/users/kennethreitz/repos?page=2&per_page=10>; rel="next", <https://api.github.com/users/kennethreitz/repos?page=6&per_page=10>; rel="last"'

Requests会自动解析这些响应头链接字段,并使得它们非常易于使用:

>>> r.links["next"]
{'url': 'https://api.github.com/users/kennethreitz/repos?page=2&per_page=10', 'rel': 'next'} >>> r.links["last"]
{'url': 'https://api.github.com/users/kennethreitz/repos?page=7&per_page=10', 'rel': 'last'}

Blocking Or Non-Blocking:阻塞、非阻塞

使用默认传输适配器,不提供任何形式的非阻塞IO请求。响应。内容属性将阻塞,直到整个反应已经被下载。如果你需要更多的粒度,库的流特性(见流式请求)允许您检索响应的小批量。然而,这些调用仍然阻止。

如果你担心使用阻塞IO,有很多的项目,将请求与Python的一个异步性框架。两个优秀的例子是grequests和requests-futures。

Timeouts:超时

大多数请求外部服务器应该有一个超时,以防服务器没有响应及时。没有超时,那么您的代码就会挂几分钟或者更多。

连接超时的秒数请求将等待你的客户建立一个连接到一个远程计算机(对应于connect())调用套接字。它是一个很好的实践设置连接超时略大于3的倍数,这是默认的TCP数据包传输窗口。

一旦客户端连接到服务器,发送HTTP请求,读取超时的秒数客户端将等待服务器发送一个响应。(具体地说,它的秒数,客户端从服务器将字节之间等待发送。在99.9%的情况下,这是时间服务器发送的第一个字节)。

如果你为超时指定一个值,如下:

r = requests.get('https://github.com', timeout=5)

超时的值将被应用到连接和读取超时。指定一个元组如果你想单独设置值:

r = requests.get('https://github.com', timeout=(3.05, 27))

如果远程服务器非常缓慢,你可以告诉请求永远等待响应,通过没有作为一个超时值,然后等待

r = requests.get('https://github.com', timeout=None)

python的扩展包requests的高级用法的更多相关文章

  1. 简学Python第七章__class面向对象高级用法与反射

    Python第七章__class面向对象高级用法与反射 欢迎加入Linux_Python学习群  群号:478616847 目录: Python中关于oop的常用术语 类的特殊方法 元类 反射 一.P ...

  2. Python之Requests的高级用法

    # 高级用法 本篇文档涵盖了Requests的一些更加高级的特性. ## 会话对象 会话对象让你能够跨请求保持某些参数.它也会在同一个Session实例发出的所有请求之间保持cookies. 会话对象 ...

  3. 爬虫 requests模块高级用法

    一 介绍 #介绍:使用requests可以模拟浏览器的请求,比起之前用到的urllib,requests模块的api更加便捷(本质就是封装了urllib3) #注意:requests库发送请求将网页内 ...

  4. Python之扩展包安装

    读者朋友,在比较新的版本(Python 2 >=2.7.9 or Python 3 >=3.4)中,pip或者easy_install 扩展包命令已经默认安装(可查看   你的安装目录\p ...

  5. Python和C++的混合编程(使用Boost编写Python的扩展包)

    想要享受更轻松愉悦的编程,脚本语言是首选.想要更敏捷高效,c++则高山仰止.所以我一直试图在各种通用或者专用的脚本语言中将c++的优势融入其中.原来贡献过一篇<c++和js的混合编程>也是 ...

  6. Python 内置函数sorted()在高级用法

    对于Python内置函数sorted(),先拿来跟list(列表)中的成员函数list.sort()进行下对比.在本质上,list的排序和内建函数sorted的排序是差不多的,连参数都基本上是一样的. ...

  7. Python切片中的误区与高级用法

    众所周知,我们可以通过索引值(或称下标)来查找序列类型(如字符串.列表.元组...)中的单个元素,那么,如果要获取一个索引区间的元素该怎么办呢? 切片(slice)就是一种截取索引片段的技术,借助切片 ...

  8. Python 数据处理扩展包: numpy 和 pandas 模块介绍

    一.numpy模块 NumPy(Numeric Python)模块是Python的一种开源的数值计算扩展.这种工具可用来存储和处理大型矩阵,比Python自身的嵌套列表(nested list str ...

  9. 芝麻HTTP: Python爬虫利器之Requests库的用法

    前言 之前我们用了 urllib 库,这个作为入门的工具还是不错的,对了解一些爬虫的基本理念,掌握爬虫爬取的流程有所帮助.入门之后,我们就需要学习一些更加高级的内容和工具来方便我们的爬取.那么这一节来 ...

随机推荐

  1. shell基础之bash

    一直单单知道部署服务器等命令,shell语言还没有用心学习过,简单的学习下以供不时之需 .sh:bash脚本文件 很多时候需要多个命令来完成一项工作,而这个工作又常常是重复的,这个时候我们自然会想到将 ...

  2. Visual Studio 2015中设计UML类图

    1.UML简介 Unified Modeling Language (UML)又称统一建模语言或标准建模语言. 简单说就是以图形方式表现模型,根据不同模型进行分类,在UML 2.0中有13种图,以下是 ...

  3. CodeForces922E DP//多重背包的二进制优化

    https://cn.vjudge.net/problem/1365218/origin 题意 一条直线上有n棵树 每棵树上有ci只鸟 在一棵树底下召唤一只鸟的魔法代价是costi 每召唤一只鸟,魔法 ...

  4. maven依赖包下载地址

    http://maven.org http://mvnrepository.com/

  5. SQL语法基础之CREATE语句

    SQL语法基础之CREATE语句 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.查看帮助信息 1>.使用“?”来查看MySQL命令的帮助信息 mysql> ? CR ...

  6. 4、JPA-EntityManager.merge()

    EntityManager#merge merge() 用于处理 Entity 的同步.即数据库的插入和更新操作 merge的几种情况 1. 若传入的是一个临时对象 package jpa.test; ...

  7. jQuery图片灯箱和视频灯箱

    在一些前端页面中经常需要文件上传,为了美观,我们经常做一个灯箱来显示我们选择的文件, 而不是简单的input标签. html 代码:这个是多图片上传 <div class="layui ...

  8. C#数据结构学习

    Collection类学习 using System; using System.Collections.Generic; using System.Linq; using System.Text; ...

  9. HDU 1088(文本处理 **)

    题意是对一段文本进行处理,如果读到 <br>,则换行:如果读到 <hr>,若当前行无字符,则输出 80 个 ’-‘ 并换行,否则在下一行输出 80 个 ’-‘ 再换行:如果一行 ...

  10. tomcat下的Cookie特殊符号问题

    案例:在项目中通过Cookie方式临时存放检索条件,不小心在Cookie值中使用了特殊符号"@",导致在服务器端无法正确解析Cookie值.之所以说"不小心", ...