第14章HTTP WEB SERVICES

第14章HTTP WEB SERVICES

一、介绍

(1)HTTP WEB SERVER就是和远程服务器交换数据,除了HTTP操作外什么都不用。使用http GET得到数据,使用http POST发送数据,使用http PUT修改数据,使用http DELETE删除数据。

(2)python3有两种不同的库用于HTTP WEB SERVICES:

  • http.client是一个底层库,它实现了RFC2616——HTTP协议。
  • urllib.request是建立在http.client之上的抽象层。它提供了用于访问http和ftp服务的标准API,自动地跟随HTTP重定向并处理一些常见形式的HTTP验证。

还有一个第三方开源库,它实现的HTTP功能比http.client全面,而且提供更好的抽象层,它就是httplib2

httplib2的安装比较简单,我下载的是0.9.2地址是:https://pypi.python.org/pypi/httplib2

安装方法在命令行终端中输入:

python3 setup.py install

二、HTTP的功能

(1)caching

这里有一个缓存是如何工作的例子,当在浏览器中访问diveintomark.org时,页面包含一个背景图wearehugh.com/m.jpg。当你的浏览器下载这张图片时,服务器包含如下HTTP头:

HTTP/1.1 200 OK
Date: Sun, 31 May 2009 17:14:04 GMT
Server: Apache
Last-Modified: Fri, 22 Aug 2008 04:28:16 GMT
ETag: "3075-ddc8d800"
Accept-Ranges: bytes
Content-Length: 12405
Cache-Control: max-age=31536000, publicExpires: Mon, 31 May 2010 17:14:04 GMT
Connection: close
Content-Type: image/jpeg

Cache-ControlExpires头告诉你的浏览器这张图片可以缓存多久,缓存到哪一天。在超时之前再次访问该资源都不会产生流量。

当超出磁盘大小或者手动操作,都可以删除缓存。技术上讲在Cache-Control头中没有private关键字的话,那么这个数据是可以缓存的!

如何公司或ISP维护一个缓存代理。当你访问网站时,你的浏览器先会在本地缓存查找,如果没找到的话,它会产生一个请求尝试从远程服务器下载。如果缓存代理拥有请求的数据,它会中断那个请求并从缓存中提取数据返回给浏览器。实际上你的请求根本没有离开公司的网络。这样你就可以拥有更快的下行速度了。

python的HTTP库不支持缓存,但是httplib2支持。

(2)最后修改缓存

HTTP/1.1 200 OK
Date: Sun, 31 May 2009 17:14:04 GMT
Server: Apache
Last-Modified: Fri, 22 Aug 2008 04:28:16 GMT
ETag: "3075-ddc8d800"
Accept-Ranges: bytes
Content-Length: 12405
Cache-Control: max-age=31536000, public
Expires: Mon, 31 May 2010 17:14:04 GMT
Connection: close
Content-Type: image/jpeg

Last-Modified,如同它的名字一样,就是最后修改时间。在第二次请求同一个数据时,可以携带If-Modified-Since在你请求中,如果数据已经更改,服务器会忽略该字段,返回新的数据,状态代码200。如果数据没有更改,服务器发回HTTP 304状态。可以使用curl测试这部分:

you@localhost:~$ curl -I -H "If-Modified-Since: Fri, 22 Aug 2008 04:28:16 GMT" http://wearehugh.com/m.jpg
HTTP/1.1 304 Not Modified
Date: Sun, 31 May 2009 18:04:39 GMT
Server: Apache
Connection: close
ETag: "3075-ddc8d800"
Expires: Mon, 31 May 2010 18:04:39 GMT
Cache-Control: max-age=31536000, public

python的HTTP库不支持这项检查,但是httplib2可以。

(3)ETag 检查

ETag是另一种完成last-modified检查的方法。使用Etags,服务器发送关于你所请求数据的哈希代码存在ETag中(哈希代的生成完全由服务器觉定,唯一要求是当数据改变时哈希值也要改变)。上面的例子中的ETAG:

HTTP/1.1 200 OK
Date: Sun, 31 May 2009 17:14:04 GMT
Server: Apache
Last-Modified: Fri, 22 Aug 2008 04:28:16 GMT
ETag: "3075-ddc8d800"
Accept-Ranges: bytes
Content-Length: 12405
Cache-Control: max-age=31536000, public
Expires: Mon, 31 May 2010 17:14:04 GMT
Connection: close
Content-Type: image/jpeg

在第二次请求相同数据时,ETag哈希包含在你请求的If-None-Match中。如果数据没有改变服务器会发回304状态。使用curl测试这部分:

you@localhost:~$ curl -I -H "If-None-Match: \"3075-ddc8d800\"" http://wearehugh.com/m.jpg ①
HTTP/1.1 304 Not Modified
Date: Sun, 31 May 2009 18:04:39 GMT
Server: Apache
Connection: close
ETag: "3075-ddc8d800"
Expires: Mon, 31 May 2010 18:04:39 GMT
Cache-Control: max-age=31536000, public

注意:ETags通常是包含在一对引号里的,而引号也是ETag值的一部分,这意味着你需要在If-None-Match中包含引号并发送到服务器。

python的http库不支持ETags,但是httplib2可以。

(4)压缩

HTTP支持几咱压缩算法。最常用的两种是gzip和deflate。当你通过HTTP请求资源时,可以要求服务器发送它的压缩格式。在你的请求中包含Accept-encoding,其中列出了哪种压缩算法是你支持的。如果服务器支持任何一个算发,它会以压缩数据回传(包含一个Content-encoding它告诉你使用哪种算法)。

对于服务端开发者的重要提示:确定一个资源压缩前后有不同的Etag。否则,缓存服务器会感到困惑并使用压缩后的数据传给客户端,而且客户不能处理这些数据。阅读apache bug 39727获得更多详细内容。

python的HTTP库不支持压缩,但是httplib2可以。

(5)重定向

URI不改变,web站点的页面移动到新的地址,甚至是web服务重新注册。可以是某个页重定向到其它页,也可以是整个站点移动到新位置。

每次从HTTP服务器请求任何类型的资源时,服务器在它的响应中包函状态代码。状态代码200意味着“你请求的页面一切正常”。状态代码404意味着“页面没找到”.状态代码3xx指示了一些重定向形式( Status codes in the 300’s indicate some form of redirection)。

HTTP有几种不同的方法指示资源已经移动。两种最常用的状态代码是302和301。状态代码302临时重定向,在Location里存放新地址。状态代码301永久重定向,在Location里存放新地址。如果你得到了302状态代码和一个新地址,HTTP会尝试使用新的地址,但下次访问相同地址时,会重试旧地址。但是如果你得到的是301状态代码和一个新地址,就会假定一直使用新地址。

当从HTTP服务器收到一个状态代码时urllib.request模块自动的“跟踪”重定向,但是它不会告诉你它做了什么。你会直接得到数据,而不会知道中间经过了重定向。urllib.request处理临时重定向和永久重定向的方法是一样的,这无论是对服务器还是客户端都不好。

httplib2处理永久重定向。它不仅会告诉你发生了永久重定向,而且会在请求它们之前跟踪它们,本地的并自动的重写重定向URL。

(6)如何在HTTP协议上获得数据

要通过HTTP下载资源,只要下载一次就可以,但如果要反复的下载同一个资源呢?让我们先看一个快速但不好的例子:

>>> import urllib.request
>>> a_url = 'http://diveintopython3.org/examples/feed.xml'
>>> data = urllib.request.urlopen(a_url).read() ①
>>> type(data) ②
<class 'bytes'>
>>> print(data)
<?xml version='1.0' encoding='utf-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>
  <title>dive into mark</title>
  <subtitle>currently between addictions</subtitle>
  <id>tag:diveintomark.org,2001-07-29:/</id>
  <updated>2009-03-27T21:56:07Z</updated>
  <link rel='alternate' type='text/html' href='http://diveintomark.org/'/>

  1. 在python中通过HTTP下载文件是一件异常简单的事;实际上只用一行就可以。urllib.request模块有一个urlopen()函数,它确定一个你想得到地址,并返回一个文件对像,可以对它使用read()得到页面的内容。
  2. urlopen().read()方法一直返回一个字节对象,而不是字符串。

让我们看一下为什么这个方法效率太低:

>>> from http.client import HTTPConnection
>>> HTTPConnection.debuglevel = 1 ①
>>> from urllib.request import urlopen
>>> response = urlopen('http://diveintopython3.org/examples/feed.xml') ②
send: b'GET /examples/feed.xml HTTP/1.1 ③
Host: diveintopython3.org ④
Accept-Encoding: identity ⑤
User-Agent: Python-urllib/3.1' ⑥
Connection: close
reply: 'HTTP/1.1 200 OK'
…further debugging information omitted…

  1. 就像我在本章开头所说的那样urllib.request依赖python其它标准库——http.client。通常你不需要直接使用http.clienturllib.request模块是自动导入的)。但在这里导入它的目的是为了在HTTPConnection类上开启debug。
  2. 现在debugging标记已经设置,HTTP的请求和响应会实时打印出来。就像看到的那样,当你请求ATOM FEED,urllib.request模块发样五行到服务器。
  3. 第一行指定了你使用的HTTP动词(GET)和资源路径。
  4. 第二行指定了请求这个FEED的域名。
  5. 第三行指定了你客户端的压缩算法。就像我们之前说的样那urllib.request默认不支持压缩。
  6. 第四行指定生成请求库的名字。默认这个是Python-urllib加上一个版本号。urllib.requesthttplib2都支持改变user-Agent,只是简单的添加User-Agent到请求中就可以,它可以覆盖默认值。

现在看一下服务器的响应:

# continued from previous example
>>> print(response.headers.as_string()) ①
Date: Sun, 31 May 2009 19:23:06 GMT ②
Server: Apache
Last-Modified: Sun, 31 May 2009 06:39:55 GMT ③
ETag: "bfe-93d9c4c0" ④
Accept-Ranges: bytes
Content-Length: 3070 ⑤
Cache-Control: max-age=86400 ⑥
Expires: Mon, 01 Jun 2009 19:23:06 GMT
Vary: Accept-Encoding
Connection: close
Content-Type: application/xml
>>> data = response.read() ⑦
>>> len(data)
3070

  1. response返回urllib.request.rulopen()函数请求的所有HTTP头,从服务器发回。它同样包函了下载数据的方法。
  2. 当服务器处理你的请求时它会告诉你。
  3. 这个响应包含了Last-Modified。
  4. 这个响应包含了ETag
  5. 数据长度是3070字节。注意什么不在这里:Content-encoding。对于你的请求,你只能接受未压缩的数据(Accept-encoding:identity)。
  6. 指定了缓存最大时间是24小时(86400秒)。
  7. 最后使用response.read()下载实际数据。使用len()函数查看下载了多少数据。

就像我们看到的那样,没有使用压缩。而实际上这个服务器是支持gzip压缩的,但HTTP压缩是可选的。我们并没有要求使用它,所以没用压缩方式传输。这意味着我们本来有可能只下载941个字节,但是我们却下载了3070个字节。

如果再次请求相同的内容,同样没有If-Modified-Since、没有If-None-Match、没有缓存并且仍然没有压缩,响应也是一样。如果只下载一次还好,但如果频繁的反复下载,效率非常的低下。

(7)是时候介绍httplib2

在使用httplib2之前,你需要先安装它,访问code.google.com/p/httplib2/并下载它的最新版本。httplib2有python2和python3两个版本。

使用httplib2创建一个httplib2.Http类的实例:

>>> import httplib2
>>> h = httplib2.Http('.cache') ①
>>> response, content = h.request('http://diveintopython3.org/examples/feed.xml') ②
>>> response.status ③
200
>>> content[:52] ④
b"<?xml version='1.0' encoding='utf-8'?>\r\n<feed xmlns="
>>> len(content)
3070

  1. httplib2的主要接口是Http对象。
  2. 一但你有了一个Http对象,取回数据只要调用request()方法,参数是你想要的数据的地址。这会对那个URL执行一个HTTP GET请求。
  3. request()方法返回两个值。第一个是httplib2.Response对象,它包函了那个服务器所有的HTTP头信息。
  4. content变量包含了实际从HTTP服务器返回的数据。返回的数据做为bytes对象而不是字符串。如果你希望它是一个字符串,那么你需要觉得字符编码并自己转换它。

提示:你可能只需要一个httplib2.Http对象,因为一个对象就可以完成不同URL的请求,只要复用它就可以了。

(8)httplib2如何处理缓存

在建立httplib2.Http对象时,一定要有一个目录名。这是因为缓存需要用到这个目录。

先执行下面这些代码,它和之前的操作没有什么不同。

# continued from the previous example
>>> response2, content2 = h.request('http://diveintopython3.org/examples/feed.xml') ①
>>> response2.status ②
200
>>> content2[:52] ③
b"<?xml version='1.0' encoding='utf-8'?>\r\n<feed xmlns="
>>> len(content2)
3070

然后把这个交互窗口关调,重新开启一个,执行如下代码:

>>> import httplib2
>>> httplib2.debuglevel = 1 ①
>>> h = httplib2.Http('.cache') ②
>>> response, content = h.request('http://diveintopython3.org/examples/feed.xml') ③
>>> len(content) ④
3070
>>> response.status ⑤
200
>>> response.fromcache ⑥
True

  1. 开启httplib2的debug功能。
  2. 创建httplib2.Http对象。
  3. 请求一个URL,什么都没发生。没有任何网络活动。
  4. 我们仍然收到了全部的数据。
  5. 并收到了一个HTTP状态码,指示这个请求已经成功。
  6. 指示响应来自httplib2的本地缓存。建立httplib2.Http对象时的目录名用来保存httplib2的所有操作过的缓存。

提示:如果要开启httplib2的debugging,需要设置模块等级常量(httplib2.debuglevel),然后创建一个新的httplib2.Http对象。如果要关闭debugging,同样需要改变模块等级常量,之后新建一个httplib2.Http对象。

你之前在这个URL请求的数据,已经成功(状态:200)。服务器的响应不仅包含了feed数据,而且还设置了缓存头信息,告知接收者可以缓存最多24小时(Cache-Control:max-age=86400)。httplib2支持缓存头信息,它在.cache目录中缓存之前的响应。因为没有超时,所以当第二次请求同一个URL时,httplib2只是返回缓存内容,并没有任何网络活动。

如果想知道一个响应是否来自缓存,可以检查response.formcache

如果不想使用本地缓存,就要使用一些HTTP的功能去确定你的请求实际是到达远程服务器的。

# continued from the previous example
>>> response2, content2 = h.request('http://diveintopython3.org/examples/feed.xml',
...   headers={'cache-control':'no-cache'}) ①
connect: (diveintopython3.org, 80) ②
send: b'GET /examples/feed.xml HTTP/1.1
Host: diveintopython3.org
user-agent: Python-httplib2/$Rev: 259 $
accept-encoding: deflate, gzip
cache-control: no-cache'
reply: 'HTTP/1.1 200 OK'
…further debugging information omitted…
>>> response2.status
200
>>> response2.fromcache ③
False
>>> print(dict(response2.items())) ④
{'status': '200',
'content-length': '3070',
'content-location': 'http://diveintopython3.org/examples/feed.xml',
'accept-ranges': 'bytes',
'expires': 'Wed, 03 Jun 2009 00:40:26 GMT',
'vary': 'Accept-Encoding',
'server': 'Apache',
'last-modified': 'Sun, 31 May 2009 22:51:11 GMT',
'connection': 'close',
'-content-encoding': 'gzip',
'etag': '"bfe-255ef5c0"',
'cache-control': 'max-age=86400',
'date': 'Tue, 02 Jun 2009 00:40:26 GMT',
'content-type': 'application/xml'}

  1. httplib2允许你添加任意HTTP头信息到request中。为了不使用缓存,添加no-cache头信息到header目录中(不仅是本地缓存,缓存代理服务器也不会有缓存)。
  2. 现在你看到了httplib2初始化了一个网络请求。因为设置了no-cache头信息,所以会跳过本地缓存并且直接从网络请求数据。
  3. 保证这个响应不是来自本地缓存。
  4. 请求成功。再次从远程服务器下载了全部的feed。它包含了缓存头信息,这被httplib2用来更新本地缓存。缓存就是设计用来缓存的最大命中和最小的网络访问。

(9)httplib2如何处理Last-Modified和ETag头信息

Cache-ControlExpires缓存头信息被称作“新鲜指示器”(freshness indicators),它们直接告诉缓存可以使用多久。但是如果数据发生了改变怎么办呢?HTTP定义了Last-ModifiedEtag就是为了这个目地。这些头信息被叫做“有效指示器”(validators)。

>>> import httplib2
>>> httplib2.debuglevel = 1
>>> h = httplib2.Http('.cache')
>>> response, content = h.request('http://diveintopython3.org/') ①
connect: (diveintopython3.org, 80)
send: b'GET / HTTP/1.1
Host: diveintopython3.org
accept-encoding: deflate, gzip
user-agent: Python-httplib2/$Rev: 259 $'
reply: 'HTTP/1.1 200 OK'
>>> print(dict(response.items())) ②
{'-content-encoding': 'gzip',
'accept-ranges': 'bytes',
'connection': 'close',
'content-length': '6657',
'content-location': 'http://diveintopython3.org/',
'content-type': 'text/html',
'date': 'Tue, 02 Jun 2009 03:26:54 GMT',
'etag': '"7f806d-1a01-9fb97900"', 'last-modified': 'Tue, 02 Jun 2009 02:51:48 GMT',
'server': 'Apache',
'status': '200',
'vary': 'Accept-Encoding,User-Agent'}
>>> len(content) ③
6657

  1. 这次是下载一个页面,它是HTML。因为这是第一次请求该页面所以会有一些网络流量。
  2. 响应中包含了多种HTTP头信息,但是没有缓存信息。不过它包含有ETagLast-Modified头信息。
  3. 本次构建的这个例子,这个页面的大小是6657字节。

# continued from the previous example
>>> response, content = h.request('http://diveintopython3.org/') ①
connect: (diveintopython3.org, 80)
send: b'GET / HTTP/1.1
Host: diveintopython3.org
if-none-match: "7f806d-1a01-9fb97900" ②
if-modified-since: Tue, 02 Jun 2009 02:51:48 GMT ③
accept-encoding: deflate, gzip
user-agent: Python-httplib2/$Rev: 259 $'
reply: 'HTTP/1.1 304 Not Modified' ④
>>> response.fromcache ⑤
True
>>> response.status ⑥
200
>>> response.dict['status'] ⑦
'304'
>>> len(content) ⑧
6657

  1. 再次请求相同的页面,使用相同的http对像。
  2. httplib2回发了一条在If-None-Match头信息中包含ETag有效标示符的请求。
  3. httplib2同样会发送Last-Modified有效标识符。
  4. 服务器收到请求后会检查有效标示符并发现该请求的页面没有改变,所以回发状态304。
  5. 客户端收到消息后注意到是状态304,并从本地缓存中读取这个页面。
  6. 这有一点点混乱。因为这里有两个状态——304(从服务器返回它引发httplib2在它的缓存中查找。)和200(这个状态是从服务器返回并且是和缓存保存在一起的。)
  7. 如果要得到从服务器返回的原生状态,那就要查看response.dict,它是一个实际头信息的字典。
  8. httplib2对像的request函数返回两个对像,第一个是服务器的响应信息,包含各种头信息,第二个实际数据。一般来说不需要知道为什么可以从缓存中得到响应数据。

(10)http2lib如何处理压缩

HTTP支持几种类型的压缩,常用的两种是gzip和deflate。httplib2这两种都支持。

>> response, content = h.request('http://diveintopython3.org/')
connect: (diveintopython3.org, 80)
send: b'GET / HTTP/1.1
Host: diveintopython3.org
accept-encoding: deflate, gzip ①
user-agent: Python-httplib2/$Rev: 259 $'
reply: 'HTTP/1.1 200 OK'
>>> print(dict(response.items()))
{'-content-encoding': 'gzip', ②
'accept-ranges': 'bytes',
'connection': 'close',
'content-length': '6657',
'content-location': 'http://diveintopython3.org/',
'content-type': 'text/html',
'date': 'Tue, 02 Jun 2009 03:26:54 GMT',
'etag': '"7f806d-1a01-9fb97900"',
'last-modified': 'Tue, 02 Jun 2009 02:51:48 GMT',
'server': 'Apache',
'status': '304',
'vary': 'Accept-Encoding,User-Agent'}

  1. 每次httplib2发送一个请求,它都包含一个Accept-Encoding头信息,用来告诉服务器它可以种理deflate或gzip的压缩。
  2. 在本例中服务器使用了gzip压缩。如果不确定是否使用了压缩,可以检查response['-content-encoding'],或者不管它。

(11)httplib2如何处理重定向

HTTP定义了两种类型的重定向:临时和永久。对于临时重定向除了跟随它之外不会做其它特别的事。httplib2做这些是自动进行的。

>>> import httplib2
>>> httplib2.debuglevel = 1
>>> h = httplib2.Http('.cache')
>>> response, content = h.request('http://diveintopython3.org/examples/feed-302.xml') ①
connect: (diveintopython3.org, 80)
send: b'GET /examples/feed-302.xml HTTP/1.1 ②
Host: diveintopython3.org
accept-encoding: deflate, gzip
user-agent: Python-httplib2/$Rev: 259 $'
reply: 'HTTP/1.1 302 Found' ③
send: b'GET /examples/feed.xml HTTP/1.1 ④
Host: diveintopython3.org
accept-encoding: deflate, gzip
user-agent: Python-httplib2/$Rev: 259 $'
reply: 'HTTP/1.1 200 OK'

  1. 当前使用的这个地址是不存在的。
  2. 这是发送一个请求。
  3. 响应了一个302错误,没找到页面。在这里并没有显示出来这个响应包含了Location头信息,它指向一个真实的URL。
  4. httplib2立即返回并“跟随”那个重定向地址:http://diveintopython3.org/examples/feed.xml

# 继续刚才的例子
>>> response ①
{'status': '200',
'content-length': '3070',
'content-location': 'http://diveintopython3.org/examples/feed.xml', ②
'accept-ranges': 'bytes',
'expires': 'Thu, 04 Jun 2009 02:21:41 GMT',
'vary': 'Accept-Encoding',
'server': 'Apache',
'last-modified': 'Wed, 03 Jun 2009 02:20:15 GMT',
'connection': 'close',
'-content-encoding': 'gzip', ③
'etag': '"bfe-4cbbf5c0"',
'cache-control': 'max-age=86400', ④
'date': 'Wed, 03 Jun 2009 02:21:41 GMT',
'content-type': 'application/xml'}

  1. 这里的response来自最终的URL。
  2. httplib2添加最终URL到response字典中做为content-location。这并不是来自服务器的头信息,它是httplib2指定的。
  3. 这个feed是压缩的。
  4. 并且是可缓存的。

response会给予你一些最终URL的信息。无论你是想要中间URL还是最终被重定向的URL,httplib2都可以为你得到那些信息。

# continued from the previous example
>>> response.previous ①
{'status': '302',
'content-length': '228',
'content-location': 'http://diveintopython3.org/examples/feed-302.xml',
'expires': 'Thu, 04 Jun 2009 02:21:41 GMT',
'server': 'Apache',
'connection': 'close',
'location': 'http://diveintopython3.org/examples/feed.xml',
'cache-control': 'max-age=86400',
'date': 'Wed, 03 Jun 2009 02:21:41 GMT',
'content-type': 'text/html; charset=iso-8859-1'}
>>> type(response) ②
<class 'httplib2.Response'>
>>> type(response.previous)
<class 'httplib2.Response'>
>>> response.previous.previous ③
>>>

  1. response.previous属性保存了重定向之前原始的URL信息。
  2. responseresponse.previous都是httplib2.Response对像。
  3. response.previous.previous代表更向前的URL。

# continued from the previous example
>>> response2, content2 = h.request('http://diveintopython3.org/examples/feed-302.xml') ①
connect: (diveintopython3.org, 80)
send: b'GET /examples/feed-302.xml HTTP/1.1 ②
Host: diveintopython3.org
accept-encoding: deflate, gzip
user-agent: Python-httplib2/$Rev: 259 $'
reply: 'HTTP/1.1 302 Found' ③
>>> content2 == content ④
True

  1. 同样的URL,同样的httplib2.Http对象,因此也会有相同的缓存。
  2. 302的响应没有被缓存,所以httplib2为相同的URL发送了其它的请求。
  3. 再次收到302。但是注意什么没发生:没有再次请求最终的URL,因为响应已经被缓存了。一旦httplib2收到302代码,它会在发出请求前先查找自己的缓存,因为缓存中包含了一个没过期的请求内容,所以不需要再次请求。
  4. 两次返回的结果是相同的。

换句话说,你不必为临时重定向作任何事情。httplib2会自动的跟随它们到另一个页面。

永久重定向也是同样的简单。

# continued from the previous example
>>> response, content = h.request('http://diveintopython3.org/examples/feed-301.xml') ①
connect: (diveintopython3.org, 80)
send: b'GET /examples/feed-301.xml HTTP/1.1
Host: diveintopython3.org
accept-encoding: deflate, gzip
user-agent: Python-httplib2/$Rev: 259 $'
reply: 'HTTP/1.1 301 Moved Permanently' ②
>>> response.fromcache ③
True

  1. 同样的连接只不过服务器端已经改成永久重定向。
  2. 这里有一个301状态码。注意这里并没有请求重定向的URL,因为已经缓存到本地了。
  3. httplib2跟随重定向到它的本地。

# continued from the previous example
>>> response2, content2 = h.request('http://diveintopython3.org/examples/feed-301.xml') ①
>>> response2.fromcache ②
True
>>> content2 == content ③
True

  1. 这里有一些不同:一旦httplib2跟随了永久重定向,那么以后对该URL的所有请求,都会直接转到最终目的,而不会先经过原始URL。
  2. 这个响应是来自缓存。
  3. 获得到了全部数据

(12)HTTP不仅限制在GET和POST

HTTP不仅限制在GET和POST方法。还有某些常用类型的请求,特别是在WEB浏览器里。而WEB服务API能做一些超越GET和POST的事,像是Twitter和Identi.ca都提供一个基于HTTP的API,用于发布和更新状态。httplib2就是这样。

# continued from the previous example
>>> from xml.etree import ElementTree as etree
>>> tree = etree.fromstring(content) ①
>>> status_id = tree.findtext('id') ②
>>> status_id
'5131472'
>>> url = 'https://identi.ca/api/statuses/destroy/{0}.xml'.format(status_id) ③
>>> resp, deleted_content = h.request(url, 'DELETE') ④

  1. 服务器返回一个XML。
  2. findtext()方法查找表达式并得到它的文本内容。本例中我们只查找<id>元素。
  3. <id>元素是基于文本的,我们可以构建一个URL去删除我们发布的状态信息。
  4. 删除消息,只要简单的输出HTTP DELETE请求到那个URL。

下面是实际上做了什么:

send: b'DELETE /api/statuses/destroy/5131472.xml HTTP/1.1 ①
Host: identi.ca
Accept-Encoding: identity
user-agent: Python-httplib2/$Rev: 259 $
'
reply: 'HTTP/1.1 401 Unauthorized' ②
send: b'DELETE /api/statuses/destroy/5131472.xml HTTP/1.1 ③
Host: identi.ca
Accept-Encoding: identity
authorization: Basic SECRET_HASH_CONSTRUCTED_BY_HTTPLIB2 ④
user-agent: Python-httplib2/$Rev: 259 $
'
reply: 'HTTP/1.1 200 OK' ⑤
>>> resp.status
200

  1. 删除这条状态消息。
  2. 未授权,不能删除。
  3. 再次发送请求。
  4. 授权信息,用户名和密码。
  5. 完成。

 

发表评论