6. 高级用法3 —— Requests中文文档

HTTP请求方式¶

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'}    

传输适配器Transport Adapters¶

从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创建子类。

示例:特定SSL版本¶

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-threadsgrequestsrequests-futuresrequests-async

Header 排序¶

在某些特殊情况下你也许需要按照次序来提供 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)    

设置的值将同时应用于connectread超时。如果要单独设置值,请指定元组:

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

如果远端服务器很慢,你可以让 Request 永远等待,传入一个 None 作为 timeout 值,然后就冲咖啡去吧。

    r = requests.get('https://github.com', timeout=None)
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值