深入Python3(十三) HTTP Web 服务

0.摘要

  简单地讲, H T T P   w e b HTTP\ web HTTP web 服务是指以编程的方式直接使用 h t t p http http 操作从远程服务器发送接收数据。如果你要从服务器获取数据,使用 h t t p   G E T http\ GET http GET;如果你要向服务器发送新数据,使用 h t t p   P O S T http\ POST http POST。一些更高级的 h t t p   W e b http\ Web http Web 服务 a p i api api也允许使用 h t t p   P U T http\ PUT http PUT h t t p   D E L E T E http\ DELETE http DELETE来创建、修改和删除数据。 换句话说, h t t p http http 协议中的“ v e r b s verbs verbs (动作)” (GET, POST, PUT 和 DELETE) 可以直接对应到应用层的操作:获取,创建,修改,删除数据。

  这个方法主要的优点是简单, 它的简单证明是受欢迎的。数据 — 通常是 x m l xml xml j s o n json json — 可以事先创建好并静态的存储下来 ,或者由服务器端脚本动态生成, 并且所有主要的编程语言(当然包括 P y t h o n Python Python)都包含 h t t p http http 库用于下载数据。调试也很方便; 由于 h t t p   w e b http\ web http web 服务中每一个资源都有一个唯一的地址(以 u r l url url的形式存在), 你可以在浏览器中加载它并且立即看到原始的数据。

   h t t p   w e b http\ web http web 服务示例:

  • Google Data api 允许你同很多类型的Google 服务交互, 包括 Blogger 和 YouTube。
  • Flickr Services 允许你向Flickr下载和上传图片。
  • Twitter api 允许你在Twitter发布状态更新。
  • …以及更多

   P y t h o n 3 Python 3 Python3 带有两个库用于和 h t t p   w e b http\ web http web 服务交互:

  • http.client 是实现了rfc 2616, http 协议的底层库.
  • urllib.request 建立在http.client之上一个抽象层。 它为访问http 和 ftp 服务器提供了一个标准的api,可以自动跟随http 重定向, 并且处理了一些常见形式的http 认证。

  那么,你应该用哪个呢?两个都不用。取而代之, 你应该使用 h t t p l i b 2 httplib2 httplib2,一个第三方的开源库,它比 h t t p . c l i e n t http.client http.client更完整的实现了 h t t p http http协议,同时比 u r l l i b . r e q u e s t urllib.request urllib.request提供了更好的抽象。

  要理解为什么 h t t p l i b 2 httplib2 httplib2是正确的选择,你必须先了解 h t t p http http

1.HTTP的特性

  有五个重要的特性所有的 h t t p http http客户端都应该支持。

1.1缓存

  关于 w e b web web服务最需要了解的一点是网络访问是极端昂贵的。我并不是指“美元”和“美分”的昂贵(虽然带宽确实不是免费的)。我的意思是需要一个非常长的时间来打开一个连接,发送请求,并从远程服务器响应。 即使在最快的宽带连接上,延迟(从发送一个请求到开始在响应中获得数据所花费的时间)仍然高于您的预期。路由器的行为不端,被丢弃的数据包,中间代理服务器被攻击 — 在公共互联网上没有沉闷的时刻(never a dull moment),并且你对此无能为力。

   C a c h e − C o n t r o l : m a x − a g e Cache-Control: max-age CacheControl:maxage 的意思是“一个星期以内都不要来烦我。” h t t p http http在设计时就考虑到了缓存。有这样一类的设备(叫做 “缓存代理服务器”) ,它们的唯一的任务是就是呆在你和世界的其他部分之间来最小化网络请求。你的公司或 i s p isp isp 几乎肯定维护着这样的缓存代理服务器, 只不过你没有意识到而已。 它们的能够起到作用是因为缓存是内建在 h t t p http http协议中的。

  这里有一个缓存如何工作的具体例子。 你通过浏览器访问diveintomark.org。该网页包含一个背景图片, wearehugh.com/m.jpg。当你的浏览器下载那张图片时,服务器的返回包含了下面的 h t t p http http 头:
在这里插入图片描述

   C a c h e − C o n t r o l Cache-Control CacheControl E x p i r e s Expires Expires 头告诉浏览器(以及任何处于你和服务器之间的缓存代理服务器) 这张图片可以缓存长达一年。 一年! 如果在明年,你访问另外一个也包含这张图片的页面,你的浏览器会从缓存中加载这张图片而不会产生任何网络活动。

  等一下,情况实际上更好。比方说,你的浏览器由于某些原因将图片从本地缓存中移除了。可能是因为没有磁盘空间了或者是你清空了缓存,不管是什么理由。然而 h t t p http http 头告诉说这个数据可以被公共缓存代理服务器缓存( C a c h e − C o n t r o l Cache-Control CacheControl头中 p u b l i c public public关键字说明这一点)。缓存代理服务器有非常庞大的存储空间,很可能比你本地浏览器所分配的大的多。

  如果你的公司或者 i s p isp isp维护着这样一个缓存代理服务器,它很可能仍然有这张图片的缓存。 当你再次访问diveintomark.org 时, 你的浏览器会在本地缓存中查找这张图片, 它没有找到, 所以它发出一个网络请求试图从远程服务器下载这张图片。但是由于缓存代理服务器仍然有这张图片的一个副本,它将截取这个请求并从它的缓存中返回这张图片。 这意味这你的请求不会到达远程服务器; 实际上, 它根本没有离开你公司的网络。这意味着更快的下载(网络跃点变少了) 和节省你公司的花费(从外部下载的数据变少了)。

  只有当每一个角色都做按协议来做时, h t t p http http缓存才能发挥作用。一方面,服务器需要在响应中发送正确的头。另一方面,客户端需要在第二次请求同样的数据前理解并尊重这些响应头。 代理服务器不是灵丹妙药,它们只会在客户端和服务器允许的情况下尽可能的聪明。

   P y t h o n Python Python h t t p http http库不支持缓存,而 h t t p l i b 2 httplib2 httplib2支持。

1.2最后修改时间的检查

  有一些数据从不改变,而另外一些则总是在变化。介于两者之间,在很多情况下数据还没变化但是将来可能会变化。 CNN.com 的供稿每隔几分钟就会更新,但我的博客的供稿可能几天或者几星期才会更新一次。在后面一种情况的时候,我不希望告诉客户端缓存我的供稿几星期,因为当我真的发表了点东西的时候,人们可能会几个星期后才能阅读到(由于他们遵循我的 c a c h e cache cache 头—“几个星期内都不用检查这个供稿”)。另一方面,如果供稿没有改变我也不希望客户端每隔1小时就来检查一下!

   h t t p http http 对于这个问题也有一个解决方案。当你第一次请求数据时,服务器返回一个 L a s t − M o d i f i e d Last-Modified LastModified头。 顾名思义:数据最后修改的时间。diveintomark.org引用的这张背景图片包含一个 L a s t − M o d i f i e d Last-Modified LastModified头。
在这里插入图片描述

  如果第二(第三,第四)次请求同样一个资源,你可以在你的请求中发送一个 I f − M o d i f i e d − S i n c e If-Modified-Since IfModifiedSince头,其值为你上次从服务器返回的时间。如果从那时开始,数据已经发生过变化,服务器会忽略 I f − M o d i f i e d − S i n c e If-Modified-Since IfModifiedSince头并返回新数据和200状态码给你。否则的话,服务器将发回一个特殊的 h t t p 304 http 304 http304 状态码, 它的含义是“从上次请求到现在数据没有发生过变化.” 你可以在命令行上使用 c u r l curl curl来测试:
在这里插入图片描述
在这里插入图片描述

  为什么这是一个进步?因为服务器发送304时, 它没有重新发送数据。你得到的仅仅是状态码。即使你的缓存副本已经过期,最后修改时间检查保证你不会在数据没有变化的情况下重新下载它。 (额外的好处是,这个304 响应同样也包含了缓存头。代理服务器会在数据已经“过期”的情况下仍然保留数据的副本; 希望数据实际上还没有改变,并且下一个请求以304状态码返回,并更新缓存信息。)

   P y t h o n Python Python h t t p http http 库不支持最后修改时间检查,而 h t t p l i b 2 httplib2 httplib2 支持。

1.3ETags

   E T a g ETag ETag 是另一个和最后修改时间检查达到同样目的的方法。使用 E T a g ETag ETag时,服务器在返回数据的同时在 E T a g ETag ETag头里返回一个哈希码(如何生成哈希码完全取决于服务器,唯一的要求是数据改变时哈希码也要改变) diveintomark.org引用的背景图片包含有 E T a g ETag ETag 头。
在这里插入图片描述

  当你再次请求同样的数据时,你在 I f − N o n e − M a t c h If-None-Match IfNoneMatch头里放入 E T a g ETag ETag值。如果数据没有发生改变,服务器将会返回304状态码。同最后修改时间检查一样,服务器发回的只有304 状态码,不会再一次给你发送同样的数据。通过在请求中包含 E T a g ETag ETag 哈希码,你告诉服务器如果哈希值匹配就不需要重新发送同样的数据了,因为你仍然保留着上次收到的数据。
在这里插入图片描述
在这里插入图片描述

  ① E T a g ETag ETag 一般使用引号包围, 但是引号是值的一部分。它们不是分隔符; E T a g ETag ETag头里面唯一的分隔符 E T a g ETag ETag 和 "3075-ddc8d800"之间的冒号。这意味着你也需要将引号放在 I f − N o n e − M a t c h If-None-Match IfNoneMatch头发回给服务器。

   P y t h o n   h t t p Python\ http Python http库不支持 E T a g ETag ETag,而 h t t p l i b 2 httplib2 httplib2支持。

1.4压缩

  当我们谈论 h t t p   w e b http\ web http web 服务的时候, 你总是会讨论到在线路上来回运送文本数据。可能是 x m l xml xml,也可能是 j s o n json json,抑或仅仅是纯文本。不管是什么格式,文本的压缩性能很好。 X M L XML XML 章节中的示例供稿在没压缩的情况下是3070 字节,然而在 g z i p gzip gzip 压缩后只有941 字节。仅仅是原始大小的30%!

   h t t p http http支持若干种压缩算法。最常见的两种是 g z i p gzip gzip d e f l a t e deflate deflate。当你通过 h t t p http http请求资源时,你可以要求服务器以压缩格式返回资源。你在请求中包含一个 A c c e p t − e n c o d i n g Accept-encoding Acceptencoding头,里面列出了你支持的压缩算法。如果服务器也支持其中的某一种算法,它就会返回给你压缩后的数据(同时通过 C o n t e n t − e n c o d i n g Content-encoding Contentencoding头标识它使用的算法)。接下来的事情就是由你去解压数据了。

   P y t h o n Python Python h t t p http http库不支持压缩,但 h t t p http httplib2支持。

1.5重定向

  好的 u r i uri uri不会变化,但是有很多 u r i uri uri并没有那么好。网站可能会重新组织,页面移动到新位置。即使是 w e b web web 服务也可能重新安排。一个联合供稿http://example.com/index.xml 可能会移动到http://example.com/xml/atom.xml。或者当一个机构扩张和重组的时候,整个域名都可能移动; http://www.example.com/index.xml 变成 http://server-farm-1.example.com/index.xml。

  每一次你向 h t t p http http服务器请求资源的时候, 服务器都会在响应中包含一个状态码。 状态码200的意思是一切正常,这就是你请求的页面; 状态码404的意思是找不到页面; (你很可能在浏览网页的时候碰到过404)。300 系列的状态码意味着某种形式的重定向。

   h t t p http http 有多种方法表示一个资源已经被移动。最常见两个技术是状态码302 和 301。 状态码 302 是一个临时重定向; 它意味着, 资源被被临时从这里移动走了; (并且临时地址在 L o c a t i o n Location Location 头里面给出)。状态码301永久重定向; 它意味着,资源被永久的移动了; (并且在 L o c a t i o n Location Location头里面给出了新的地址)。如果你得到302状态码和一个新地址, h t t p http http规范要求你访问新地址来获得你要的资源,但是下次你要访问同样的资源的时候你应该重新尝试旧的地址。但是如果你得到301状态码和新地址, 你从今以后都应该使用新的地址。

   u r l l i b . r e q u e s t urllib.request urllib.request模块在从 h t t p http http服务器收到对应的状态码的时候会自动“跟随”重定向, 但它不会告诉你它这么干了。你最后得到了你请求的数据,但是你永远也不会知道下层的库友好的帮助你跟随了重定向。结果是,你继续访问旧的地址,每一次你都会得到新地址的重定向,每一次 u r l l i b . r e q u e s t urllib.request urllib.request模块都会友好的帮你跟随重定向。换句话说,它将永久重定向当成临时重定向来处理。这意味着两个来回而不是一个,这对你和服务器都不好。

   h t t p l i b 2 httplib2 httplib2 帮你处理了永久重定向。它不仅会告诉你发生了永久重定向,而且它会在本地记录这些重定向,并且在发送请求前自动重写为重定向后的 u r l url url

2.避免通过 HTTP 重复地获取数据

  我们来举个例子,你想要通过 h t t p http http下载一个资源, 比如说一个Atom 供稿。作为一个供稿, 你不会只下载一次,你会一次又一次的下载它。 (大部分的供稿阅读器会每一小时检查一次更新。) 让我们先用最粗糙和最快的方法来实现它,接着再来看看怎样改进。
在这里插入图片描述

  ①在 P y t h o n Python Python中通过 h t t p http http下载东西是非常简单的; 实际上,只需要一行代码。 u r l l i b . r e q u e s t urllib.request urllib.request模块有一个方便的函数 u r l o p e n ( ) urlopen() urlopen() ,它接受你所要获取的页面地址,然后返回一个类文件对象,您只要调用它的 r e a d ( ) read() read()方法就可以获得网页的全部内容。没有比这更简单的了。
  ② u r l o p e n ( ) . r e a d ( ) urlopen().read() urlopen().read()方法总是返回 b y t e s bytes bytes对象,而不是字符串。记住字节仅仅是字节,字符只是一种抽象。 h t t p http http 服务器不关心抽象的东西。如果你请求一个资源,你得到字节。 如果你需要一个字符串,你需要确定字符编码,并显式的将其转化成字符串。

  那么,有什么问题呢?作为开发或测试中的快速试验,没有什么不妥的地方。我总是这么干。我需要供稿的内容,然后我拿到了它。相同的技术对任何网页都有效。但一旦你考虑到你需要定期访问 W e b Web Web服务的时候,(例如每隔1小时请求一下这个供稿), 这样的做法就显得很低效和粗暴了。

3.线路上是什么?

  为了说明为什么这是低效和粗暴的,我们来打开 P y t h o n Python Python h t t p http http库的调试功能,看看什么东西被发送到了线路上(即网络上)。
在这里插入图片描述

  ①正如我在这章开头提到的, u r l l i b . r e q u e s t urllib.request urllib.request 依赖另一个标准Python库, h t t p . c l i e n t http.client http.client。正常情况下你不需要直接接触 h t t p . c l i e n t http.client http.client。 ( u r l l i b . r e q u e s t urllib.request urllib.request 模块会自动导入它。) 我们在这里导入它是为了让我们能够打开 H T T P C o n n e c t i o n HTTPConnection HTTPConnection类的调试开关, u r l l i b . r e q u e s t urllib.request urllib.request 使用这个类去连接 h t t p http http服务器。
  ②调试开关已经打开,有关 h t t p http http请求和响应的信息会实时的打印出来。正如你所看见的,当你请求 A t o m Atom Atom 供稿时, u r l l i b . r e q u e s t urllib.request urllib.request模块向服务器发送了5行数据。
  ③第一行指定了你使用的 h t t p http http方法和你访问的资源的路径(不包含域名)。
  ④第二行指定了你请求的供稿所在的域名。
  ⑤第三行指定客户端支持的压缩算法。我之前提到过, u r l l i b . r e q u e s t urllib.request urllib.request 默认不支持压缩。
  ⑥第四行说明了发送请求的库的名字。默认情况下是 P y t h o n − u r l l i b Python-urllib Pythonurllib加上版本号。 u r l l i b . r e q u e s t urllib.request urllib.request h t t p l i b 2 httplib2 httplib2都支持更改用户代理, 直接向请求里面加一个 U s e r − A g e n t User-Agent UserAgent头就可以了(默认值会被覆盖)。
  本地用3.7版本测试,好像已经没有调试用的输出了。
在这里插入图片描述

  ① u r l l i b . r e q u e s t . u r l o p e n ( ) urllib.request.urlopen() urllib.request.urlopen()函数返回的 r e s p o n s e response response对象包含了服务器返回的所有 h t t p http http头。它也提供了下载实际数据的方法,这个我们等一下讲。
  ②服务器提供了它处理你的请求时的时间。
  ③这个响应包含了 L a s t − M o d i f i e d Last-Modified LastModified头。
  ④这个响应包含了 E T a g ETag ETag头。
  ⑤数据的长度是3070字节。请注意什么东西没有出现在这里: C o n t e n t − e n c o d i n g Content-encoding Contentencoding头。你的请求表示你只接受未压缩的数据,(Accept-encoding: identity), 然后当然,响应确实包含未压缩的数据。
  ⑥ 这个响应包含缓存头,表明这个供稿可以缓存长达24小时。(86400 秒).
  ⑦ 最后,通过调用 r e s p o n s e . r e a d ( ) response.read() response.read()下载实际的数据. 你从 l e n ( ) len() len()函数可以看出,一下子就把整个3070个字节下载下来了。

  正如你所看见的,这个代码已经是低效的了;它请求(并接收)了未压缩的数据。
在这里插入图片描述

  注意到这个请求有什么特别之处吗?它没有变化。它同第一个请求完全一样。没有If-Modified-Since头。没有If-None-Match头。
在这里插入图片描述

  ①服务器仍然在发送同样的聪明的头: Cache-Control 和 Expires 用于允许缓存, Last-Modified 和 ETag用于“是否变化”的跟踪。甚至是Vary: Accept-Encoding头暗示只要你请求,服务器就能支持压缩。但是你没有。
  ②再一次,获取这个数据下载了一共3070个字节…
  ③…和你上一次下载的3070字节完全一致。

   h t t p http http 设计的能比这样工作的更好。 u r l l i b urllib urllib使用 h t t p http http就像我说西班牙语一样 — 可以表达基本的意思,但是不足以保持一个对话。 h t t p http http 是一个对话。是时候更新到一个可以流利的讲 h t t p http http的库了。

4.介绍 httplib2

  首先通过 p i p   i n s t a l l   h t t p l i b 2 pip\ install\ httplib2 pip install httplib2安装这个库。
在这里插入图片描述

  ① h t t p l i b 2 httplib2 httplib2的主要接口是 H t t p Http Http对象。你创建 H t t p Http Http对象时总是应该传入一个目录名,具体原因你会在下一节看见。目录不需要事先存在, h t t p l i b 2 httplib2 httplib2会在必要的时候创建它。
  ②一旦你有了 H t t p Http Http对象, 获取数据非常简单,以你要的数据的地址作为参数调用 r e q u e s t ( ) request() request()方法就可以了。这会对该 u r l url url执行一个 h t t p   G E T http\ GET http GET请求. (这一章下面你会看见怎样执行其他 h t t p http http 请求, 比如 P O S T POST POST。)
  ③ r e q u e s t ( ) request() request() 方法返回两个值。第一个是一个 h t t p l i b 2. R e s p o n s e httplib2.Response httplib2.Response对象,其中包含了服务器返回的所有 h t t p http http头。比如, s t a t u s status status为200 表示请求成功。
  ④ c o n t e n t content content 变量包含了 h t t p http http服务器返回的实际数据。数据以 b y t e s bytes bytes对象返回,不是字符串。 如果你需要一个字符串,你需要确定字符编码并自己进行转换。

4.1关于httplib2返回字节串而不是字符串的简短解释

  字节串。字符串。真麻烦啊。为什么 h t t p l i b 2 httplib2 httplib2不能替你把转换做了呢?由于决定字符编码的规则依赖于你请求的资源的类型,导致自动转化很复杂。 h t t p l i b 2 httplib2 httplib2怎么知道你要请求的资源的类型呢?通常类型会在 C o n t e n t − T y p e   h t t p Content-Type\ http ContentType http 头里面列出,但是这是 h t t p http http的可选特性,并且并非所有的 h t t p http http服务器都支持。如果 h t t p http http响应没有包含这个头,那就留给客户端去猜了。

  如果你知道你期待的资源是什么类型的(这个例子中是 x m l xml xml文档), 也许你应该直接将返回的字节串( b y t e s bytes bytes)对象传给 x m l . e t r e e . E l e m e n t T r e e . p a r s e ( ) xml.etree.ElementTree.parse() xml.etree.ElementTree.parse() 函数。只要(像这个文档一样) x m l xml xml 文档自己包含字符编码信息,这是可以工作的。但是字符编码信息是一个可选特性并非所有 x m l xml xml文档包含这样的信息。如果一个 x m l xml xml文档不包含编码信息,客户端应该去查看 C o n t e n t − T y p e   h t t p Content-Type\ http ContentType http 头, 里面应该包含一个 c h a r s e t charset charset参数。

4.2httplib2怎样处理缓存

  还记的在前一节我说过你总是应该在创建 h t t p l i b 2. H t t p httplib2.Http httplib2.Http对象时提供一个目录名吗? 缓存就是这样做的目的。
在这里插入图片描述

  ①让我们打开调试开关来看看线路上是什么。这是使用httplib2打开http.client调试开关的方法. httplib2会打印出发给服务器的所有数据以及一些返回的关键信息。
  ②使用同之前一样的目录创建httplib2.Http对象。
  ③请求同之前一样的url。 什么也没有发生。 更准确的说,没有东西发送到服务器,没有东西从服务器返回。没有任何形式的网络活动。
  ④但我们还是接收到了数据,实际上是所有的数据。
  ⑤我们也接收到表示请求成功的http状态码。
  ⑥这里是奥秘所在: 响应是从httplib2的本地缓存构造出来的。你创建httplib2.Http对象时传入的目录里面保存了所有httplib2执行过的操作的缓存

  本地测试确实会有一个这样的文件:
在这里插入图片描述

  你刚刚请求过这个 u r l url url的数据。那个请求是成功的(状态码: 200)。该响应不仅包含 f e e d feed feed数据,也包含一系列缓存头,告诉那些关注着的人这个资源可以缓存长达24小时(Cache-Control: max-age=86400, 24小时所对应的秒数)。 h t t p l i b 2 httplib2 httplib2 理解并尊重那些缓存头,并且它会在 . c a c h e .cache .cache目录(你在创建 H t t p Http Http对象时提供的)保存之前的响应。缓存还没有过期,所以你第二次请求该 u r l url url的数据时,$ httplib2$不会去访问网络,直接返回缓存着的数据。

  我说的很简单,但是很显然在这简单后面隐藏了很多复杂的东西。 h t t p l i b 2 httplib2 httplib2会自动处理 h t t p http http缓存,并且这是默认的行为. 如果由于某些原因你需要知道响应是否来自缓存,你可以检查 r e s p o n s e . f r o m c a c h e response.fromcache response.fromcache

  现在,假设你有数据缓存着,但是你希望跳过缓存并且重新请求远程服务器。浏览器有时候会应用户的要求这么做。比如说,按 F 5 F5 F5刷新当前页面,但是按 C t r l + F 5 Ctrl+F5 Ctrl+F5会跳过缓存并向远程服务器重新请求当前页面。你可能会想“嗯,我只要从本地缓存删除数据,然后再次请求就可以了。” 你可以这么干,但是请记住, 不只是你和远程服务器会牵扯其中。那些中继代理服务器呢? 它们完全不受你的控制,并且它们可能还有那份数据的缓存,然后很高兴的将其返回给你, 因为(对它们来说)缓存仍然是有效的。

  你应该使用 h t t p http http的特性来保证你的请求最终到达远程服务器,而不是修改本地缓存然后听天由命。
在这里插入图片描述

  ① h t t p l i b 2 httplib2 httplib2 允许你添加任意的 h t t p http http头部到发出的请求里。为了跳过所有缓存(不仅仅是你本地的磁盘缓存,也包括任何处于你和远程服务器之间的缓存代理服务器), 在 h e a d e r s headers headers字典里面加入 n o − c a c h e no-cache nocache头就可以了。
  ② 现在你可以看见 h t t p l i b 2 httplib2 httplib2初始化了一个网络请求。 h t t p l i b 2 httplib2 httplib2 理解并尊重两个方向的缓存头, — 作为接受的响应的一部分以及作为发出的请求的一部分. 它注意到你加入了一个 n o − c a c h e no-cache nocache头,所以它完全跳过了本地的缓存,然后不得不去访问网络来请求数据。
  ③ 这个响应不是从本地缓存生成的。你当然知道这一点,因为你看见了发出的请求的调试信息。但是从程序上再验证一下也不错。
  ④ 请求成功;你再次从远程服务器下载了整个供稿。当然,服务器同供稿数据一起也返回了完整的 h t t p http http头。这里面也包含缓存头, h t t p l i b 2 httplib2 httplib2会使用它来更新它的本地缓存,希望你下次请求该供稿时能够避免网络请求。 h t t p http http缓存被设计为尽量最大化缓存命中率和最小化网络访问。即使你这一次跳过了缓存,服务器仍非常乐意你能缓存结果以备下一次请求。

4.3httplib2怎么处理Last-Modified和ETag头

   C a c h e − C o n t r o l Cache-Control CacheControl E x p i r e s Expires Expires 缓存头被称为新鲜度指标(freshness indicators)。他们毫不含糊告诉缓存,你可以完全避免所有网络访问,直到缓存过期。而这正是你在前一节所看到的: 给出一个新鲜度指标, h t t p l i b 2 httplib2 httplib2 不会产生哪怕是一个字节的网络活动 就可以提供缓存了的数据(当然除非你显式的要求跳过缓存).

  那如果数据可能已经改变了, 但实际没有呢? h t t p http http 为这种目的定义了 L a s t − M o d i f i e d Last-Modified LastModified E t a g Etag Etag头。 这些头被称为验证器(validators)。如果本地缓存已经不是新鲜的,客户端可以在下一个请求的时候发送验证器来检查数据实际上有没有改变。如果数据没有改变,服务器返回304状态码,但不返回数据。 所以虽然还会在网络上有一个来回,但是你最终可以少下载一点字节。
在这里插入图片描述

  ① 取代供稿,我们这一次要下载的是网站的主页,是 h t m l html html格式的。这是你第一次请求这个页面, h t t p l i b 2 httplib2 httplib2没什么能做的,它在请求中发出最少量的头。
  ② 响应包含了多个 h t t p http http头… 但是没有缓存信息。然而,它包含了 E T a g ETag ETag L a s t − M o d i f i e d Last-Modified LastModified头。
  ③ 在我写这个例子的时候,这个页面有6657字节。在那之后,它很可能已经变了, 但是不用担心这一点。
在这里插入图片描述

  ① 你再次请求同一个页面,使用同一个 H t t p Http Http对象(以及同一个本地缓存)。
  ② h t t p l i b 2 httplib2 httplib2 E T a g   v a l i d a t o r ETag\ validator ETag validator 通过 I f − N o n e − M a t c h If-None-Match IfNoneMatch头发送回服务器。
  ③ h t t p l i b 2 httplib2 httplib2 也将 L a s t − M o d i f i e d   v a l i d a t o r Last-Modified\ validator LastModified validator 通过 I f − M o d i f i e d − S i n c e If-Modified-Since IfModifiedSince头发送回服务器。
  ④ 服务器查看这些验证器( v a l i d a t o r s validators validators), 查看你请求的页面,然后判读得出页面在上次请求之后没有改变过, 所以它发回了304 状态码不带数据.
  ⑤ 回到客户端, h t t p l i b 2 httplib2 httplib2 注意到304状态码并从它的缓存加载页面的内容。
  ⑥ 这可能会让人有些困惑。这里实际上有两个 状态码 — 304 (服务器这次返回的, 导致 h t t p l i b 2 httplib2 httplib2查看它的缓存), 和 200 (服务器上次返回的, 并和页面数据一起保存在 h t t p l i b 2 httplib2 httplib2的缓存里)。 r e s p o n s e . s t a t u s response.status response.status返回缓存里的那个。
  ⑦ 如果你需要服务器返回的原始的状态码,你可以从 r e s p o n s e . d i c t response.dict response.dict里面找到, 它是包含服务器返回的真实头部的字典.
  ⑧ 然而,数据还是保存在了$content4变量里。一般来说,你不需要关心为什么响应是从缓存里面来的。(你甚至不需要知道它是从缓存里来的, 这是一件好事。 h t t p l i b 2 httplib2 httplib2 足够聪明,允许你傻瓜一点。) r e q u e s t ( ) request() request()返回的时候, h t t p l i b 2 httplib2 httplib2就已经更新了缓存并把数据返回给你了。

4.4http2lib怎么处理压缩

在这里插入图片描述

  ① 每一次 h t t p l i b 2 httplib2 httplib2 发送请求,它包含了 A c c e p t − E n c o d i n g Accept-Encoding AcceptEncoding头来告诉服务器它能够处理 d e f l a t e deflate deflate 或者 g z i p gzip gzip压缩。
  ② 这个例子中,服务器返回了 g z i p gzip gzip压缩过的负载,当 r e q u e s t ( ) request() request()方法返回的时候, h t t p l i b 2 httplib2 httplib2就已经解压缩了响应的体(body)并将其放在 c o n t e n t content content变量里。如果你想知道响应是否压缩过, 你可以检查response[’-content-encoding’]。

4.5httplib2怎样处理重定向

   h t t p http http 定义了 两种类型的重定向: 临时的和永久的。对于临时重定向,除了跟随它们其他没有什么特别要做的, h t t p l i b 2 httplib2 httplib2 会自动处理跟随。
在这里插入图片描述

  ① 这个 u r l url url上没有供稿。我设置了服务器让其发出一个到正确地址的临时重定向。
  ② 这是请求。
  ③ 这是响应: 302   F o u n d 302\ Found 302 Found。这里没有显示出来,这个响应也包含由一个 L o c a t i o n Location Location头给出实际的 u r l url url
  ④ h t t p l i b 2 httplib2 httplib2 马上转身并跟随重定向,发出另一个到在 L o c a t i o n 头 Location头 Location里面给出的url: http://diveintopython3.org/examples/feed.xml 的请求。

  “跟随” 一个重定向就是这个例子展示的那么多。 h t t p l i b 2 httplib2 httplib2 发送一个请求到你要求的 u r l url url。服务器返回一个响应说“不,不, 看那边。” h t t p l i b 2 httplib2 httplib2 给新的 u r l url url发送另一个请求。
在这里插入图片描述

  你得到的 r e s p o n s e response response给了你最终 u r l url url的相关信息。如果你希望那些最后重定向到最终 u r l url url的中间 u r l url url的信息呢? h t t p l i b 2 httplib2 httplib2 也能帮你。
在这里插入图片描述
在这里插入图片描述

  ③ 再一次,服务器以302响应。但是请注意什么没有发生: 没有第二个到最终 u r l url url, http://diveintopython3.org/examples/feed.xml 的请求。原因是缓存 (还记的你在前一个例子中看到的Cache-Control头吗?)。 一旦 h t t p l i b 2 httplib2 httplib2 收到 302   F o u n d 302\ Found 302 Found 状态码, 它在发出新的请求前检查它的缓存。 缓存中有http://diveintopython3.org/examples/feed.xml的一份新鲜副本, 所以不需要重新请求它了。
  ④ 当 r e q u e s t ( ) request() request()方法返回的时候,它已经从缓存中读取了 f e e d feed feed数据并返回了它。当然,它和你上次收到的数据是一样的。

  换句话说,对于临时重定向你不需要做什么特别的处理。 h t t p l i b 2 httplib2 httplib2 会自动跟随它们,而一个 u r l url url重定向到另一个这个事实上不会影响 h t t p l i b 2 httplib2 httplib2对压缩,缓存, E T a g s ETags ETags, 或者任何其他 h t t p http http特性的支持。
在这里插入图片描述
在这里插入图片描述

5.HTTP GET之外

   h t t p   w e b http\ web http web 服务并不限于 G E T GET GET请求。当你要创建点东西的时候呢?当你在论坛上发表一个评论,更新你的博客,在 T w i t t e r Twitter Twitter 或者 I d e n t i . c a Identi.ca Identi.ca这样的微博客上面发表状态消息的时候, 你很可能已经使用了 h t t p   P O S T http\ POST http POST
在这里插入图片描述

  怎么操作呢?要在 I d e n t i . c a Identi.ca Identi.ca 发布一条消息, 你需要提交一个 h t t p   P O S T http\ POST http POST请求到http://identi.ca/api/statuses/update.format. ( f o r m a t format format字样不是 u r l url url的一部分; 你应该将其替换为你希望服务器返回的请求的格式。所以如果需要一个 x m l xml xml格式的返回。你应该向https://identi.ca/api/statuses/update.xml发送请求。) 请求需要一个参数 s t a t u s status status, 包含了你的状态更新文本。并且请求必须是已授权的。

  授权? 当然。要在 I d e n t i . c a Identi.ca Identi.ca上发布你的状态更新, 你得证明你的身份。 I d e n t i . c a Identi.ca Identi.ca 不是一个维基; 只有你自己可以更新你的状态。 I d e n t i . c a Identi.ca Identi.ca 使用建立在 s s l ssl ssl之上的 h t t p   B a s i c   A u t h e n t i c a t i o n http\ Basic\ Authentication http Basic Authentication (也就是RFC 2617) 来提供安全但方便的认证。 h t t p l i b 2 httplib2 httplib2 支持 s s l ssl ssl h t t p   B a s i c   A u t h e n t i c a t i o n http\ Basic\ Authentication http Basic Authentication, 所以这部分很简单。

   P O S T POST POST 请求同 G E T GET GET 请求不同, 因为它包含负荷(payload)。负荷是你要发送到服务器的数据。这个 a p i api api方法必须的参数是 s t a t u s status status, 并且它应该是 u r l url url编码过的。 这是一种很简单的序列化格式,将一组键值对(比如字典)转化为一个字符串。
在这里插入图片描述

  ① 这是 h t t p l i b 2 httplib2 httplib2处理认证的方法。 a d d _ c r e d e n t i a l s ( ) add\_credentials() add_credentials()方法记录你的用户名和密码。当 h t t p l i b 2 httplib2 httplib2 试图执行请求的时候,服务器会返回一个401 Unauthorized状态码, 并且列出所有它支持的认证方法(在 WWW-Authenticate 头中)。 h t t p l i b 2 httplib2 httplib2会自动构造Authorization头并且重新请求该 u r l url url
  ② 第二个参数是 h t t p http http请求的类型。这里是 P O S T POST POST
  ③ 第三个参数是要发送到服务器的负荷 。我们发送包含状态消息的 u r l url url编码过的字典。
  ④ 最后,我们得告诉服务器负荷是 u r l url url编码过的数据。

   a d d _ c r e d e n t i a l s ( ) add\_credentials() add_credentials()方法的第三个参数是该证书有效的域名。你应该总是指定这个参数! 如果你省略了这个参数,并且之后重用这个 h t t p l i b 2. H t t p httplib2.Http httplib2.Http对象访问另一个需要认证的站点,可能会导致 h t t p l i b 2 httplib2 httplib2将一个站点的用户名密码泄漏给其他站点。
在这里插入图片描述
在这里插入图片描述

6.HTTP POST之外

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/xiji333/article/details/110848285