Python 標準庫中的 urllib2 模塊提供了你所須要的大多數 HTTP 功能,可是它的 API 太渣了。它是爲另外一個時代、另外一個互聯網所建立的。它須要巨量的工做,甚至包括各類方法覆蓋,來完成最簡單的任務。html
Requests 徹底知足現在網絡的需求。python
會話就是session,session的實現是基於cookie的,因此會話對象可以跨請求保持一些參數,也能夠在同一個session實例發出的全部請求之間保持cookies。git
import requests s = requests.Session() s.get('http://httpbin.org/cookies/set/sessioncookie/123456789') r=s.get('http://httpbin.org/cookies')
會話對象也能夠爲其你去提供缺省數據,經過會話對象的屬性提哦給你數據來實現的github
s = requests.Session() s.auth = ('user', 'pass') s.headers.update({'x-test': 'true'}) # both 'x-test' and 'x-test2' are sent s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})
會話對象傳入的參數會自動覆蓋初始化中的默認參數。web
任什麼時候候調用requests.*()你都在作兩件主要的事情。其一,你在構建一個 Request 對象, 該對象將被髮送到某個服務器請求或查詢一些資源。其二,一旦 requests 獲得一個從 服務器返回的響應就會產生一個 Response 對象。該響應對象包含服務器返回的全部信息, 也包含你原來建立的 Request 對象。json
r = requests.get('http://www.baidu.com') r.headers
獲取響應頭的信息。查看http協議瞭解響應頭的內容api
下面的代碼是獲取請求的內容瀏覽器
r.request.headers
當你從一個api請求或者一個session請求接受到一個響應對象的時候,請求的參數其實是PreparedRequest 對象使用的,若是你想在提交請求以前在請求體body或者請求頭header作一些操做的時候。安全
from requests import Request, Session s = Session() req = Request('GET', url, data=data, headers=header ) prepped = req.prepare() # do something with prepped.body # do something with prepped.headers resp = s.send(prepped, stream=stream, verify=verify, proxies=proxies, cert=cert, timeout=timeout ) print(resp.status_code)
能夠理解。Requests對象其實就是當PreparedRequest 沒有被修改時直接提交的。最後的請求方式是 requests.* 或者 Session.*.服務器
上面的代碼在使用Requests的session對象時極可能會丟失一些優點,session級別的狀態,好比說cookie就不會在request請求中,用Session.prepare_request()代替Request.prepare() 就能夠完美的實現session級別的狀態。
from requests import Request, Session s = Session() req = Request('GET', url, data=data headers=headers ) prepped = s.prepare_request(req) # do something with prepped.body # do something with prepped.headers resp = s.send(prepped, stream=stream, verify=verify, proxies=proxies, cert=cert, timeout=timeout ) print(resp.status_code)
SSL證書經過在客戶端瀏覽器和Web服務器之間創建一條SSL安全通道(Secure socket layer(SSL)安全協議是由Netscape Communication公司設計開發。該安全協議主要用來提供對用戶和服務器的認證;對傳送的數據進行加密和隱藏;確保數據在傳送中不被改變,即數據的完整性,現已成爲該領域中全球化的標準。因爲SSL技術已創建到全部主要的瀏覽器和WEB服務器程序中,所以,僅需安裝服務器證書就能夠激活該功能了)。即經過它能夠激活SSL協議,實現數據信息在客戶端和服務器之間的加密傳輸,能夠防止數據信息的泄露。保證了雙方傳遞信息的安全性,並且用戶能夠經過服務器證書驗證他所訪問的網站是不是真實可靠。
Requests能夠爲HTTPS請求驗證SSL證書,就像web瀏覽器同樣。要想檢查某個主機的SSL證書,你可使用 verify 參數:
requests.get('https://kennethreitz.com', verify=True)
在該域名上我沒有設置SSL,因此失敗了。但Github設置了SSL:
requests.get('https://github.com', verify=True)
若是你將 verify 設置爲False,Requests也能忽略對SSL證書的驗證
requests.get('https://kennethreitz.com', verify=False)
默認狀況下, verify 是設置爲True的。選項 verify 僅應用於主機證書。
你也能夠指定一個本地證書用做客戶端證書,能夠是單個文件(包含密鑰和證書)或一個包含兩個文件路徑的元組:
requests.get('https://kennethreitz.com', cert=('/path/server.crt', '/path/key'))
若是你指定了一個錯誤路徑或一個無效的證書:
requests.get('https://kennethreitz.com', cert='/wrong_path/server.pem') SSLError: [Errno 336265225] _ssl.c:347: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib
默認狀況下,當你進行網絡請求後,響應體會當即被下載。你能夠經過 stream 參數覆蓋這個行爲,推遲下載響應體直到訪問 Response.content 屬性:
tarball_url = 'https://github.com/kennethreitz/requests/tarball/master' r = requests.get(tarball_url, stream=True)
此時僅有響應頭被下載下來了,鏈接保持打開狀態,所以容許咱們根據條件獲取內容:
if int(r.headers['content-length']) < TOO_LONG: content = r.content ...
當講一個request請求的stream=True時,鏈接connection不會被返回放入到鏈接池中去除非你讀取完requests中全部的數據或者直接調用Requests.close()方法。這必然致使鏈接的效率很低,當只須要響應的正文部分(或者什麼都不須要讀取),能夠試試contextlib.closing
from contextlib import closing with closing(requests.get('http://httpbin.org/get', stream=True)) as r: # Do things with the response here.
好消息 - 歸功於urllib3,同一會話內的持久鏈接是徹底自動處理的!同一會話內你發出的任何請求都會自動複用恰當的鏈接!只有全部的響應體數據被讀取完畢鏈接纔會被釋放爲鏈接池;因此確保將 stream設置爲 False 或讀取 Response 對象的 content 屬性。
Requests支持流式上傳,這容許你發送大的數據流或文件而無需先把它們讀入內存。要使用流式上傳,僅需爲你的請求體提供一個類文件對象便可:
with open('massive-body') as f: requests.post('http://some.url/streamed', data=f)
對於出去和進來的請求,Requests也支持分塊傳輸編碼。要發送一個塊編碼的請求,僅需爲你的請求體提供一個生成器(或任意沒有具體長度(without a length)的迭代器)
def gen(): yield 'hi' yield 'there' requests.post('http://some.url/chunked', data=gen())
當須要在一次請求中上傳多個文件的時候,如:
<input type=」file」 name=」images」 multiple=」true」 required=」true」/>
這時候能夠將文件設置成一個元組列表(文件名稱,文件信息)。
url = 'http://httpbin.org/post' multiple_files = [('images', ('foo.png', open('foo.png', 'rb'), 'image/png')), ('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))] r = requests.post(url, files=multiple_files) r.text
使用 requests.Response.iter_lines() 你能夠很方便地對流式API(例如 Twitter的流式API )進行迭代。簡單地設置 stream 爲 True 即可以使用 iter_lines() 對相應進行迭代:
import json import requests r = requests.get('http://httpbin.org/stream/20', stream=True) for line in r.iter_lines(): # filter out keep-alive new lines if line: print(json.loads(line))
若是須要使用代理,你能夠經過爲任意請求方法提供 proxies 參數來配置單個請求:
import requests proxies = { "http": "http://10.10.1.10:3128", "https": "http://10.10.1.10:1080", } requests.get("http://example.org", proxies=proxies)
你也能夠經過環境變量 HTTP_PROXY 和 HTTPS_PROXY 來配置代理。
$ export HTTP_PROXY="http://10.10.1.10:3128" $ export HTTPS_PROXY="http://10.10.1.10:1080" $ python import requests requests.get("http://example.org")
若你的代理須要使用HTTP Basic Auth,可使用 http://user:password@host/ 語法:
proxies = { "http": "http://user:pass@10.10.1.10:3128/", }
當你收到一個響應時,Requests會猜想響應的編碼方式,用於在你調用 Response.text方法時 對響應進行解碼。Requests首先在HTTP頭部檢測是否存在指定的編碼方式,若是不存在,則會使用 charade 來嘗試猜想編碼方式
只有當HTTP頭部不存在明確指定的字符集,而且 Content-Type 頭部字段包含 text 值之時, Requests纔不去猜想編碼方式
在這種狀況下, RFC 2616 指定默認字符集 必須是 ISO-8859-1 。Requests聽從這一規範。若是你須要一種不一樣的編碼方式,你能夠手動設置 Response.encoding 屬性,或使用原始的 Response.content
Requests提供了幾乎全部HTTP動詞的功能:GET,OPTIONS, HEAD,POST,PUT,PATCH和DELETE。 如下內容爲使用Requests中的這些動詞以及Github API提供了詳細示例。
我將從最常使用的動詞GET開始。HTTP GET是一個冪等的方法,從給定的URL返回一個資源。於是, 當你試圖從一個web位置獲取數據之時,你應該使用這個動詞。一個使用示例是嘗試從Github上獲取 關於一個特定commit的信息。假設咱們想獲取Requests的commit a050faf 的信息。咱們能夠 這樣去作
import requests r = requests.get('https://api.github.com/repos/kennethreitz/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 方法把這個返回的數據解析成Python對象
>>> commit_data = r.json() >>> print commit_data.keys() [u'committer', u'author', u'url', u'tree', u'sha', u'parents', u'message'] >>> print commit_data[u'committer'] {u'date': u'2012-05-10T11:10:50-07:00', u'email': u'me@kennethreitz.com', u'name': u'Kenneth Reitz'} >>> print commit_data[u'message'] makin' history
到目前爲止,一切都很是簡單。嗯,咱們來研究一下GitHub的API。咱們能夠去看看文檔, 但若是使用Requests來研究也許會更有意思一點。咱們能夠藉助Requests的OPTIONS動詞來看看咱們剛使用過的url 支持哪些HTTP方法
>>> verbs = requests.options(r.url) >>> verbs.status_code 500
額,這是怎麼回事?毫無幫助嘛!原來GitHub,與許多API提供方同樣,實際上並未實現OPTIONS方法。 這是一個惱人的疏忽,但不要緊,那咱們可使用枯燥的文檔。然而,若是GitHub正確實現了OPTIONS, 那麼服務器應該在響應頭中返回容許用戶使用的HTTP方法,例如
>>> verbs = requests.options('http://a-good-website.com/api/cats') >>> print verbs.headers['allow'] GET,HEAD,POST,OPTIONS
轉而去查看文檔,咱們看到對於提交信息,另外一個容許的方法是POST,它會建立一個新的提交。 因爲咱們正在使用Requests代碼庫,咱們應儘量避免對它發送笨拙的POST。做爲替代,咱們來 玩玩GitHub的Issue特性
>>> r = requests.get('https://api.github.com/repos/kennethreitz/requests/issues/482') >>> r.status_code 200 >>> issue = json.loads(r.text) >>> print issue[u'title'] Feature any http verb in docs >>> print issue[u'comments'] 3
使用https://api.github.com/repos/kennethreitz/requests/issues/482爲例
r = requests.get('https://api.github.com/repos/kennethreitz/requests/issues/482') >>> r.status_code 200 >>> issue = json.loads(r.text) >>> print issue[u'title'] Feature any http verb in docs >>> print issue[u'comments']
有3個評論。咱們來看一下最後一個評論
>>> r = requests.get(r.url + u'/comments') >>> r.status_code 200 >>> comments = r.json() >>> print comments[0].keys() [u'body', u'url', u'created_at', u'updated_at', u'user', u'id'] >>> print comments[2][u'body'] Probably in the "advanced" section
嗯,那看起來彷佛是個愚蠢之處。咱們發表個評論來告訴這個評論者他本身的愚蠢。那麼,這個評論者是誰呢?
>>> print comments[2][u'user'][u'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/kennethreitz/requests/issues/482/comments" >>> r = requests.post(url=url, data=body) >>> r.status_code 404
這有點古怪哈。可能咱們須要驗證身份。那就有點糾結了,對吧?不對。Requests簡化了多種身份驗證形式的使用, 包括很是常見的Basic Auth
>>> 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[u'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/kennethreitz/requests/issues/comments/5804413" >>> r = requests.patch(url=url, data=body, auth=auth) >>> r.status_code 200
很是好。如今,咱們來折磨一下這個叫肯尼思的傢伙,我決定要讓他急得團團轉,也不告訴他是我在搗蛋。 這意味着我想刪除這條評論。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' ...
許多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'}
使用默認傳輸適配器,不提供任何形式的非阻塞IO請求。響應。內容屬性將阻塞,直到整個反應已經被下載。若是你須要更多的粒度,庫的流特性(見流式請求)容許您檢索響應的小批量。然而,這些調用仍然阻止。
若是你擔憂使用阻塞IO,有不少的項目,將請求與Python的一個異步性框架。兩個優秀的例子是grequests和requests-futures。
大多數請求外部服務器應該有一個超時,以防服務器沒有響應及時。沒有超時,那麼您的代碼就會掛幾分鐘或者更多。
鏈接超時的秒數請求將等待你的客戶創建一個鏈接到一個遠程計算機(對應於connect())調用套接字。它是一個很好的實踐設置鏈接超時略大於3的倍數,這是默認的TCP數據包傳輸窗口。
一旦客戶端鏈接到服務器,發送HTTP請求,讀取超時的秒數客戶端將等待服務器發送一個響應。(具體地說,它的秒數,客戶端從服務器將字節之間等待發送。在99.9%的狀況下,這是時間服務器發送的第一個字節)。
若是你爲超時指定一個值,以下:
r = requests.get('https://github.com', timeout=5)
超時的值將被應用到鏈接和讀取超時。指定一個元組若是你想單獨設置值:
r = requests.get('https://github.com', timeout=(3.05, 27))
若是遠程服務器很是緩慢,你能夠告訴請求永遠等待響應,經過沒有做爲一個超時值,而後等待
r = requests.get('https://github.com', timeout=None)