RESTful API 系列这个坑,打算最近稍微填一下,我觉得有以下3个坑去填,都是关于Python的requests模块的,或者说是requests模块的一个最浅薄的深入探析。

  1. requests的http响应是怎么个情况
  2. requests的其他调用方法,delete、put(patch)
  3. requests去调用带认证体系的API时如何处理

今天我们先填第一个坑,我们调用一个RESTful API或者是一个HTTP接口的时候,一般是怎么样的一个响应

在这3个坑之间,我打算再水两篇,继续讲讲netconf,因为很多人后台咨询我这个东西,这玩意,说实话,坑多,网上的东西抄来抄去,或者只是简单调用,真正花点功夫去了解的人不多,我花了功夫也有些细节不了解,不过应该可以击败这些拾人牙慧的分享。有些坑我也没走出来,比如openconfig,华为设备明明显示支持,为什么我就是调用不出来,生气摊手懒得弄了。

言归正传,开始讲解今天的requests的http响应,如何从中提取数据。

提取响应的文本内容

我们能读取服务器响应的内容。再次以 GitHub 时间线为例:

>>> import requests
>>> r = requests.get('https://api.github.com/events')
>>> r.text
'[{"repository":{"open_issues":0,"url":"https://github.com/...

我们请求http之后,会返回一个http response的响应,我们赋值给r。

http响应r会有一个字段,text,如果返回值是文本类的,比如html、json、xml这种plan text(非二进制文件),Requests 会自动解码来自服务器的内容。大多数都能被无缝地解码。

对于以下内容如果初学者觉得引起不适,可跳过,暂时没什么大影响

请求发出后,Requests 会基于 HTTP 头部对响应的编码作出有根据的推测。当你访问 r.text 之时,Requests 会使用其推测的文本编码。你可以找出 Requests 使用了什么编码,并且能够使用r.encoding 属性来改变它:

>>> r.encoding
'utf-8'
>>> r.encoding = 'ISO-8859-1'

如果你改变了编码,每当你访问 r.text ,Request 都将会使用 r.encoding 的新值。你可能希望在使用特殊逻辑计算出文本的编码的情况下来修改编码。比如 HTTP 和 XML 自身可以指定编码。这样的话,你应该使用 r.content 来找到编码,然后设置 r.encoding 为相应的编码。这样就能使用正确的编码解析 r.text 了。

在你需要的情况下,Requests 也可以使用定制的编码。如果你创建了自己的编码,并使用 codecs模块进行注册,你就可以轻松地使用这个解码器名称作为 r.encoding 的值, 然后由 Requests 来为你处理编码。

如果返回的不是文本,还强制使用,会爆出一些异常。

不适部分结束,上述是从响应中提取文本内容。

二进制响应内容

有时候响应的是图片或者文件或者文本,我们想提取二进制内容,你也能以字节的方式访问请求响应体,对于文本非文本都可以提取二进制响应内容:

>>> r.content
b'[{"repository":{"open_issues":0,"url":"https://github.com/...HTTP

我们只要调用content属性即可,我们发现这是一个json的字符串,但是你会发现是b开头,代表是二进制的内容。

如果响应是一个文件,比如图片,你可以使用如下代码,保存成图片或者任意后缀的文件:

with open('file.jpg','wb') as f:
        f.write(r.content)

这块可以参考我们之前的文件操作篇,写文件操作。

以上主要针对下载一些附件,非文本内容。

JSON 响应内容

对于API,目前主流返回的是json,不完全,也有xml,我们今天主要介绍json。

Requests 中有一个内置的 JSON 解码器,助你处理 JSON 数据:

>>> import requests

>>> r = requests.get('https://api.github.com/events')
>>> r.json()
[{'repository': {'open_issues': 0, 'url': 'https://github.com/...

注意,这个时候我们调用的是json()这个函数方法,不是一个属性字段,一定要注意

如果 JSON 解码失败, r.json() 就会抛出一个异常。例如,响应内容是 401 (Unauthorized),尝试访问 r.json() 将会抛出 ValueError: No JSON object could be decoded 异常。

需要注意的是,成功调用 r.json()意味着响应的成功。有的服务器会在失败的响应中包含一个 JSON 对象(比如 HTTP 500 的错误细节)。这种 JSON 会被解码返回。要检查请求是否成功,请使用 r.raise_for_status() 或者检查 r.status_code 是否和你的期望相同。

调用json()如果成功,返回的应该是一个Python对象,我们可以按照处理字典或者列表的方式进行处理。

比如从控制器获取信息,或者网管平台获取信息一般返回的json,用这个方法会比较香,我们不用再json.loads(r.text)了,太费劲,多写了不少字母。

原始响应内容

在罕见的情况下,你可能想获取来自服务器的原始套接字响应,那么你可以访问 r.raw。如果你确实想这么干,那请你确保在初始请求中设置了 stream=True。具体你可以这么做:

>>> r = requests.get('https://api.github.com/events', stream=True)
>>> r.raw
<requests.packages.urllib3.response.HTTPResponse object at 0x101194810>
>>> r.raw.read(10)
'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'

但一般情况下,你应该以下面的模式将文本流保存到文件:

with open(filename, 'wb') as fd:
    for chunk in r.iter_content(chunk_size):
        fd.write(chunk)

使用 Response.iter_content 将会返回一个可迭代的对象,它是逐步生成的,不会占用大量内存,会每次从源读取一部分内容,然后按照chunk_size读取下一段内容,我们可以按需设置这个size的大小。它是读取bytes的个数,默认是1。


附上一段实际可执行的代码吧

import json
import requests
if __name__ == '__main__':

    r = requests.get('http://httpbin.org/stream/20', stream=True)

# for line in r.iter_lines():
#     '''
#     当使用 decode_unicode=True 在 Response.iter_lines() 或 Response.iter_content() 中时,
#     你需要提供一个回退编码方式,以防服务器没有提供默认回退编码,从而导致错误:
#     '''
#     # filter out keep-alive new lines
#     if line:
#         decoded_line = line.decode('utf-8')
#         print(json.loads(decoded_line))
# for content in r.iter_content(chunk_size=1024):
for content in r.iter_content(chunk_size=104):
        print(content)

好了,这次的分享就到这里,几点说明,github的api大家不要调用了 是https的,我建议大家使用这个网址的进行练习