Requests提供了所有HTTP请求方式:GET、OPTIONS、HEAD、POST、PUT、PATCH、DELETE。以下内容为使用Requests进行各种Github API请求提供了详细示例。 我们将从最常用的请求方式开始:GET。HTTP GET是一个幂等方法,从给定URL返回资源。因此,当您试图从从一个 web 位置获取数据时,应该使用它。以下示例是尝试从GitHub获取有关特定提交的信息。假设我们想获取提交a050faf的信息,可以这样做:
>>> import requests >>> r = requests.get('https://api.github.com/repos/psf/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<requests.Response.json>方法将其解析为Python对象。
>>> commit_data = r.json() >>> print(commit_data.keys()) ['committer', 'author', 'url', 'tree', 'sha', 'parents', 'message'] >>> print(commit_data['committer']) {'date': '2012-05-10T11:10:50-07:00', 'email': 'me@kennethreitz.com', 'name': 'Kenneth Reitz'} >>> print(commit_data['message']) makin' history到目前为止,很简单。嗯,我们来研究一下 GitHub 的 API。我们可以看看文档,但如果使用请求Requests可能会有更多的乐趣。我们可以利用Requests的OPTIONS来查看我们刚刚使用的URL支持哪些类型的HTTP方法。
>>> verbs = requests.options(r.url) >>> verbs.status_code 500呃,这是怎么回事?毫无帮助嘛!原来GitHub和很多其他API提供方一样,实际上并没有实现OPTIONS方法。这是一个恼人的疏忽,但没关系,我们可以使用无聊的文档。但是,如果GitHub有正确实现的选项,它们应该在响应头中返回允许用户使用的 HTTP 方法,例如:
>>> verbs = requests.options('http://a-good-website.com/api/cats') >>> print(verbs.headers['allow']) GET,HEAD,POST,OPTIONS再看一下文档,我们看到唯一允许提交的方法是POST,它创建了一个新的提交。由于我们正在使用 Requests 代码库,我们应尽可能避免对它发送笨拙的 POST。相反,我们来试试 GitHub 的 Issue 特性。 鉴于Issue #482问题已经存在,我们就以它为例,首先获取它:
>>> r = requests.get('https://api.github.com/repos/psf/requests/issues/482') >>> r.status_code 200 >>> issue = json.loads(r.text) >>> print(issue['title']) Feature any http verb in docs >>> print(issue['comments']) 3很好,我们有三条评论,让我们看看最后一个。
>>> r = requests.get(r.url + '/comments') >>> r.status_code 200 >>> comments = r.json() >>> print(comments[0].keys()) ['body', 'url', 'created_at', 'updated_at', 'user', 'id'] >>> print(comments[2]['body']) Probably in the "advanced" section嗯,那地方看起来很愚蠢。我们发表个评论来告诉这个评论者他自己的愚蠢。那么,这个评论者是谁呢?
>>> print(comments[2]['user']['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/psf/requests/issues/482/comments" >>> r = requests.post(url=url, data=body) >>> r.status_code 404额,奇怪。我们可能需要认证。那会很痛苦,对吧?错了,Requests很容易使用多种形式的身份验证,包括非常常见的基本身份验证。
>>> 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['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/psf/requests/issues/comments/5804413" >>> r = requests.patch(url=url, data=body, auth=auth) >>> r.status_code 200非常好。现在,我们来折磨一下这个叫 Kenneth 的家伙,我决定要让他急得团团转,也不告诉他是我在捣蛋。这意味着我想删除这条评论。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': '4995' 'x-ratelimit-limit': '5000' ...很好。是时候写个 Python 程序以各种刺激的方式滥用 GitHub 的 API,还可以使用 4995 次呢。
有时候你会碰到一些服务器,处于某些原因,它们允许或者要求用户使用上述 HTTP 请求动词之外的自定义制动词。比如说 WEBDAV 服务器会要求你使用 MKCOL 方法。别担心,Requests 一样可以搞定它们。你可以使用内建的 .request方法,例如:
>>> r = requests.request('MKCOL', url, data=data) >>> r.status_code 200 # Assuming your call was correct这样您可以使用任何服务器允许的请求动词方法。
许多 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'}从v1.0.0 以后,Requests 的内部采用了模块化设计。部分原因是为了实现传输适配器(Transport Adapter),你可以看看关于它的最早描述。传输适配器提供了一个机制,让你可以为 HTTP 服务定义交互方法。尤其是它允许你应用服务前的配置。 Requests 自带了一个传输适配器,也就是HTTPAdapter。 这个适配器使用了强大的 urllib3,为 Requests 提供了默认的 HTTP 和 HTTPS 交互。每当 Session 被初始化,就会有适配器附着在 Session 上,其中一个供 HTTP 使用,另一个供 HTTPS 使用。 Requests允许用户创建和使用自己提供特定功能的传输适配器。一旦创建了传输适配器,就可以将其加载到会话对象上,并指出它应用于哪些web服务。
>>> s = requests.Session() >>> s.mount('https://github.com/', MyAdapter())这个mount调用会注册一个传输适配器的特定实例到一个前缀上面。加载以后,任何使用该会话的 HTTP 请求,只要其 URL 是以给定的前缀开头,该传输适配器就会被调用。 传输适配器的众多实现细节不在本文档的覆盖范围内,不过你可以看看接下来这个简单的 SSL 用例。如果需要更多用法,也许该考虑为BaseAdapter创建子类。
Requests团队已经做出了一个特定的选择,即使用底层库(urllib3)中默认的SSL版本。通常情况下,这是可以的,但有时,您可能会发现自己需要连接到与默认版本不兼容的服务端。 您可以使用传输适配器来实现这一点,方法是使用HTTPAdapter的大部分现有实现,并添加一个传递给 urllib3 的参数ssl_version。我们将编写一个指定SSLv3版本的传输适配器:
import ssl from urllib3.poolmanager import PoolManager from requests.adapters import HTTPAdapter class Ssl3HttpAdapter(HTTPAdapter): """"Transport adapter" that allows us to use SSLv3.""" def init_poolmanager(self, connections, maxsize, block=False): self.poolmanager = PoolManager( num_pools=connections, maxsize=maxsize, block=block, ssl_version=ssl.PROTOCOL_SSLv3)使用默认的传输适配器,Requests 不提供任何形式的非阻塞 IO。Response.content属性会阻塞,直到整个响应下载完成。如果你需要更多精细控制,该库的数据流功能(见 流式请求) 允许你每次接受少量的一部分响应,不过这些调用依然是阻塞式的。 如果你对于阻塞式 IO 有所顾虑,还有很多结合Requests 和某个异步框架的项目可以供你使用。典型的优秀项目如requests-threads、grequests、requests-futures和requests-async。
在某些特殊情况下你也许需要按照次序来提供 header,如果你向 headers 关键字参数传入一个OrderedDict,就可以向提供一个带排序的 header。然而,Requests 使用的默认 header 的次序会被优先选择,这意味着如果你在 headers 关键字参数中覆盖了默认 header,和关键字参数中别的 header 相比,它们也许看上去会是次序错误的。 如果这个对你来说是个问题,那么应该考虑在 Session 对象上面设置默认 header,只要将 Session 设为一个定制的 OrderedDict 即可,这样就会让它成为首选。
为防止服务器不能及时响应,大部分发至外部服务器的请求都应该带着 timeout 参数。除非显式指定了 timeout 值,requests 默认是不会自动进行超时处理的。如果没有 timeout,你的代码可能会挂起若干分钟甚至更长时间。 连接超时指的是客户端连接到远端服务器端口时(对应的是connect())Request 会等待的秒数。一个很好的实践方法是把连接超时设为比 3 的倍数略大的一个数值,因为 TCP 数据包重传窗口 (TCP packet retransmission window) 的默认大小是 3。 一旦你的客户端连接到了服务器并且发送了 HTTP 请求,读取超时指的就是客户端等待服务器发送请求的时间。(特定地,它指的是客户端要等待服务器发送字节之间的时间。在 99.9% 的情况下这指的是服务器发送第一个字节之前的时间)。 如果为超时指定一个值,如下所示:
r = requests.get('https://github.com', timeout=5)设置的值将同时应用于connect和read超时。如果要单独设置值,请指定元组:
r = requests.get('https://github.com', timeout=(3.05, 27))如果远端服务器很慢,你可以让 Request 永远等待,传入一个 None 作为 timeout 值,然后就冲咖啡去吧。
r = requests.get('https://github.com', timeout=None)