溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

怎么使用Python的Requests庫(kù)

發(fā)布時(shí)間:2023-05-11 11:00:55 來(lái)源:億速云 閱讀:102 作者:iii 欄目:編程語(yǔ)言

本文小編為大家詳細(xì)介紹“怎么使用Python的Requests庫(kù)”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“怎么使用Python的Requests庫(kù)”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來(lái)學(xué)習(xí)新知識(shí)吧。

快速開(kāi)始

發(fā)送請(qǐng)求
>>> import requests
>>> r = requests.get('https://api.github.com/events') # GET
>>> r = requests.post('https://httpbin.org/post', data={'key': 'value'}) # POST
>>> r = requests.put('https://httpbin.org/put', data={'key': 'value'}) # PUT
>>> r = requests.delete('https://httpbin.org/delete') # DELETE
>>> r = requests.head('https://httpbin.org/get') # HEAD 
>>> r = requests.options('https://httpbin.org/get') # OPTIONS
URL傳參

可以使用params字典參數(shù)為URL提供查詢(xún)字符串參數(shù),例如,訪問(wèn) https://httpbin.org/get?key1=value1&key2=value2,可使用以下代碼:

>>> import requests
>>> payload = {'key1': 'value1', 'key2': 'value2', 'key3':'', 'key4':None}
>>> r = requests.get('https://httpbin.org/get', params=payload)
>>> r.url
https://httpbin.org/get?key2=value2&key1=value1&key3=

需要注意的是,如果字典參數(shù)中key值(即URL參數(shù)的值為None),則該參數(shù)不會(huì)添加到URL的查詢(xún)字符串中。

如果URL查詢(xún)字符串中,存在重復(fù)參數(shù)(參數(shù)名稱(chēng)相同,參數(shù)值不同),則需要將key值設(shè)置為由參數(shù)值組成的列表,如下:

>>> import requests
>>> payload = {'key1': 'value1', 'key2': ['value2', 'value3']}
>>> r = requests.get('https://httpbin.org/get', params=payload)
>>> r.url
https://httpbin.org/get?key1=value1&key2=value2&key2=value3
響應(yīng)內(nèi)容

讀取服務(wù)器響應(yīng)內(nèi)容

>>> import requests
>>> r = requests.get('https://api.github.com/events')
>>> r.text
<class 'str'> [{"id":"27579847062","type":"PushEvent","actor":{"...

requests 將自動(dòng)解碼來(lái)自服務(wù)器的內(nèi)容。大多數(shù)unicode字符集都是無(wú)縫解碼的。

當(dāng)你發(fā)出請(qǐng)求時(shí),requests會(huì)根據(jù)HTTP頭對(duì)響應(yīng)的編碼進(jìn)行有依據(jù)的猜測(cè)。當(dāng)你訪問(wèn)r.text時(shí),將使用requests猜測(cè)的文本編碼。可以使用r.encoding屬性查找請(qǐng)求使用的編碼,并對(duì)其進(jìn)行更改:

>>> r.encoding # 輸出:utf-8
r.encoding = 'ISO-8859-1'

如果更改編碼,則每當(dāng)調(diào)用r.text時(shí),requests都將使用新的r.encoding的值。在任何情況下,你都可以應(yīng)用特殊邏輯來(lái)確定內(nèi)容的編碼。例如,HTML和XML可以在其正文中指定其編碼。在這種情況下,你應(yīng)該使用r.content查找編碼,然后設(shè)置r.encoding。這將允許你使用具有正確編碼的r.text

requests還將在需要時(shí)使用自定義編碼。如果你已經(jīng)創(chuàng)建了自己的編碼并將其注冊(cè)到codecs模塊,則可以簡(jiǎn)單地使用codec名稱(chēng)作為r.encoding的值,而requests將為你處理解碼。

二進(jìn)制響應(yīng)內(nèi)容

對(duì)于非文本請(qǐng)求,還可以以字節(jié)的形式訪問(wèn)響應(yīng)體(當(dāng)然,文本請(qǐng)求也可以):

>>> r.content
b'[{"id":"27581220674","type":"IssueCommentEvent","actor":{"id":327807...

requests會(huì)自動(dòng)解碼gzipdeflate傳輸編碼。

如果安裝了類(lèi)似 brotli 或 brotlicffi的Brotil類(lèi)庫(kù),Requets也會(huì)自動(dòng)界面br傳輸編碼

如果Brotli庫(kù)(如[Brotli])為您自動(dòng)解碼br傳輸編碼(https://pypi.org/project/brotli)或brotliffi已安裝。

例如,可以使用以下代碼,從請(qǐng)求返回的二進(jìn)制數(shù)據(jù)創(chuàng)建圖像:

from PIL import Image
from io import BytesIO
img = Image.open(BytesIO(r.content))
JSON響應(yīng)內(nèi)容

可使用內(nèi)置的JSON解碼器,處理JSON數(shù)據(jù):

>>> import requests
>>> r = requests.get('https://api.github.com/events')
>>> r.json() # JSON
[{'id': '27609416600', 'type': 'PushEvent', ...

如果JSON解碼失敗,r.json()將拋出異常。例如,如果響應(yīng)得到一個(gè)204(無(wú)內(nèi)容),或者如果響應(yīng)包含無(wú)效的JSON,則r.json()會(huì)拋出requests.exceptions.JSONDecodeError。此封裝的異??赡軙?huì)因?yàn)椴煌琾ython版本和JSON序列化庫(kù)可能引發(fā)的多個(gè)異常提供互操作性。

需要注意的是,調(diào)用r.json()的成功調(diào)用并不表示響應(yīng)的成功。一些服務(wù)器可能會(huì)在失敗的響應(yīng)中返回JSON對(duì)象(例如,HTTP 500的錯(cuò)誤詳細(xì)信息)。這樣的JSON將被解碼并返回。要檢查請(qǐng)求是否成功,請(qǐng)使用r.raise_for_status()或檢查r.status_code

原始響應(yīng)內(nèi)容

可以通過(guò)訪問(wèn)r.raw訪問(wèn)服務(wù)器返回的原始socket響應(yīng)。如果希望這樣做,確保在初始請(qǐng)求中設(shè)置 stream=True:

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

然而,通常情況下,應(yīng)該使用類(lèi)似這樣的模式來(lái)保存正在流式傳輸?shù)膬?nèi)容到文件中:

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

使用Response.iter_content將處理很多你在直接使用Resort.raw時(shí)需要處理的事情。當(dāng)流式傳輸下載時(shí),以上是檢索內(nèi)容的首選和推薦方法。請(qǐng)注意,chunk_size可以自由調(diào)整為更適合你使用場(chǎng)景的數(shù)字。

注意

關(guān)于使用 Response.iter_contentResponse.raw的重要注意事項(xiàng)。 Response.iter_content將自動(dòng)解碼gzipdeflate傳輸編碼。Response.raw是一個(gè)原始字節(jié)流--它不會(huì)轉(zhuǎn)換響應(yīng)內(nèi)容。如果確實(shí)需要訪問(wèn)返回的字節(jié),請(qǐng)使用Response.raw。

自定義請(qǐng)求頭

如果您想向請(qǐng)求中添加HTTP頭,只需向headers參數(shù)傳遞一個(gè)dict即可,例如:

>>> url = 'https://api.github.com/some/endpoint'
>>> headers = {'user-agent': 'my-app/0.0.1'}
>>> r = requests.get(url, headers=headers)

注意:自定義請(qǐng)求頭的優(yōu)先級(jí)低于更具體的信息源。例如:

  • 如果在.netrc中指定了憑據(jù),則使用headers=設(shè)置的Authorization請(qǐng)求頭將被覆蓋,而憑據(jù)又將被auth=參數(shù)覆蓋。請(qǐng)求將在~/.netrc、~/_netrcNETRC環(huán)境變量指定的路徑處中搜索netrc文件。

  • 如果從主機(jī)重定向,將刪除Authorization請(qǐng)求頭。

  • Proxy-Authorization請(qǐng)求頭將被URL中提供的代理憑據(jù)覆蓋。

  • 當(dāng)我們可以確定內(nèi)容的長(zhǎng)度時(shí),將覆蓋Content-Length請(qǐng)求頭。

此外,請(qǐng)求根本不會(huì)根據(jù)指定的自定義請(qǐng)求頭更改其行為。請(qǐng)求頭僅是簡(jiǎn)單的傳遞到最終請(qǐng)求中。

注意:所有請(qǐng)求頭值必須是字符串、字節(jié)字符串或unicode。雖然允許,但建議避免傳遞unicode請(qǐng)求頭值。

更復(fù)雜的POST請(qǐng)求More complicated POST requests

通常,如果發(fā)送一些表單編碼(form-encoded)的數(shù)據(jù)--就像一個(gè)HTML表單。為此,只需將字典傳遞給data參數(shù)即可。發(fā)送請(qǐng)求時(shí),將自動(dòng)對(duì)字典數(shù)據(jù)進(jìn)行表單編碼:

>>> import requests
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.post("https://httpbin.org/post", data=payload)
>>> r.text
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "key1": "value1", 
    "key2": "value2"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "23", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.27.1", 
    "X-Amzn-Trace-Id": "Root=1-6409fe3b-0cb4118319f09ab3187402bc"
  }, 
  "json": null, 
  "origin": "183.62.127.25", 
  "url": "https://httpbin.org/post"
}

data參數(shù)中,為每個(gè)鍵可以具有多個(gè)值。這可以通過(guò)將data設(shè)置為元組列表或以列表為值的字典來(lái)實(shí)現(xiàn)。當(dāng)表單中有多個(gè)元素使用相同的鍵時(shí),這特別有用:

>>> import requests
>>> payload_tuples = [('key1', 'value1'), ('key1', 'value2')]
>>> r1 = requests.post('https://httpbin.org/post', data=payload_tuples)
>>> payload_dict = {'key1': ['value1', 'value2']}
>>> r2 = requests.post('https://httpbin.org/post', data=payload_dict)
>>> r1.text
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "key1": [
      "value1", 
      "value2"
    ]
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "23", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.27.1", 
    "X-Amzn-Trace-Id": "Root=1-6409ff49-11b8232a7cc81fc0290ec4c4"
  }, 
  "json": null, 
  "origin": "183.62.127.25", 
  "url": "https://httpbin.org/post"
}
>>> re.text == r2.text
True

有時(shí),你可能想發(fā)送未經(jīng)表單編碼的數(shù)據(jù),則需要傳入string類(lèi)型的數(shù)據(jù),而不是dict,string數(shù)據(jù)將被直接提交。

例如,GitHub API v3接受JSON編碼的POST/PATCH數(shù)據(jù):

>>> import requests
>>> import json
>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'}
>>> r = requests.post(url, data=json.dumps(payload))

請(qǐng)注意,上述代碼不會(huì)添加Content-Type請(qǐng)求頭(特別是不會(huì)將其設(shè)置為application/json)。如果需要設(shè)置那個(gè)請(qǐng)求頭('Content-Type': 'application/json,發(fā)送json請(qǐng)求體),并且不想自己對(duì)dict進(jìn)行編碼,你也可以直接使用json參數(shù)傳遞它,它將自動(dòng)被編碼:

>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'}
>>> r = requests.post(url, json=payload)

注意,如果提供了data,或者file參數(shù),json 參數(shù)將被自動(dòng)忽略。

提交Multipart-Encoded文件

Request讓上傳Multipart編碼文件變得簡(jiǎn)單:

>>> import requests
>>> url = 'https://httpbin.org/post'
>>> files = {'file': open('report.xls', 'rb')}
>>> r = requests.post(url, files=files)
>>> r.text
{
  "args": {}, 
  "data": "", 
  "files": {
    "file": "#!/usr/bin/env python\r\n# -*- coding:utf-8 -*-\r\n\r\n#!/usr/bin/env python\r\n# -*- coding:utf-8 -*-\r\n\r\nfrom multiprocessing import Pool\r\nfrom threading import Thread\r\nfrom concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor..."
  }, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "3035", 
    "Content-Type": "multipart/form-data; boundary=9ef4437cb1e14427fcba1c42943509cb", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.27.1", 
    "X-Amzn-Trace-Id": "Root=1-640a03df-1a0a5ce972ce410378cda7a2"
  }, 
  "json": null, 
  "origin": "183.62.127.25", 
  "url": "https://httpbin.org/post"
}

可以顯示的設(shè)置文件名稱(chēng),內(nèi)容類(lèi)型,請(qǐng)求頭:

>>> url = 'https://httpbin.org/post'
files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel',  {'Expires': '0'})}
>>> r = requests.post(url, files=files)
>>> r.text
{
  "args": {}, 
  "data": "", 
  "files": {
    "file": "data:application/vnd.ms-excel;base64,UEsDBBQAAAAAAHy8iFMAAAAAAA...=="
  }, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "9667", 
    "Content-Type": "multipart/form-data; boundary=ff85e1018eb5232f7dcab2b2bc5ffa50", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.27.1", 
    "X-Amzn-Trace-Id": "Root=1-640def51-43cc213e33437a0e60255add"
  }, 
  "json": null, 
  "origin": "183.62.127.25", 
  "url": "https://httpbin.org/post"
}

如果想發(fā)送一些字符串,以文件的方式被接收:

>>> url = 'https://httpbin.org/post'
>>> files = {'file': ('report.csv', 'some,data,to,send\nanother,row,to,send\n')}
>>> r = requests.post(url, files=files)
>>> r.text
{
  "args": {}, 
  "data": "", 
  "files": {
    "file": "some,data,to,send\nanother,row,to,send\n"
  }, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "184", 
    "Content-Type": "multipart/form-data; boundary=2bfe430e025860528e29c893a09f1198", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.27.1", 
    "X-Amzn-Trace-Id": "Root=1-640df132-247947ca699e9da35c588f2d"
  }, 
  "json": null, 
  "origin": "183.62.127.25", 
  "url": "https://httpbin.org/post"
}

如果你將一個(gè)非常大的文件作為multipart/form-data請(qǐng)求提交,你可能需要流式傳輸該請(qǐng)求。默認(rèn)情況下,requests不支持此功能,但有一個(gè)單獨(dú)的包支持此功能&mdash;&mdash;requests toolbelt。閱讀toolbelt文檔獲取有關(guān)如何使用它的詳細(xì)信息。

要在一個(gè)請(qǐng)求中發(fā)送多個(gè)文件,請(qǐng)參閱高級(jí)章節(jié)。

警告

強(qiáng)烈建議以二進(jìn)制模式打開(kāi)文件。這是因?yàn)閞equests可能會(huì)嘗試為你提供Content-Length請(qǐng)求頭,如果這樣做,該請(qǐng)求頭值將被設(shè)置為文件中的字節(jié)數(shù)。如果以文本模式打開(kāi)文件,可能會(huì)發(fā)生錯(cuò)誤。

響應(yīng)狀態(tài)碼
>>> import requests
>>> r = requests.get('https://httpbin.org/get')
>>> r.status_code
200

以便于參考,requests還附帶一個(gè)內(nèi)置的狀態(tài)代碼查找對(duì)象:

>>> r = requests.get('https://httpbin.org/get')
>>> r.status_code == requests.codes.ok
True

如果請(qǐng)求出錯(cuò)4XX客戶(hù)端錯(cuò)誤或5XX服務(wù)器錯(cuò)誤響應(yīng)),我們可以使用response.raise_for_status()拋出錯(cuò)誤:

>>> import requests
>>> bad_r = requests.get('https://httpbin.org/status/404')
>>> bad_r.status_code
404
>>> bad_r.raise_for_status()
Traceback (most recent call last):
  File "D:/codePojects/test.py", line 12, in <module>
    bad_r.raise_for_status()
  File "D:\Program Files (x86)\python36\lib\site-packages\requests\models.py", line 960, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: NOT FOUND for url: https://httpbin.org/status/404

但是,如果r.status_code200, raise_for_status() 將返回None

>>> r.raise_for_status()
None
響應(yīng)頭
>>> r.headers
{
    'content-encoding': 'gzip',
    'transfer-encoding': 'chunked',
    'connection': 'close',
    'server': 'nginx/1.0.4',
    'x-runtime': '148ms',
    'etag': '"e1ca502697e5c9317743dc078f67693f"',
    'content-type': 'application/json'
}

根據(jù)RFC 7230, HTTP請(qǐng)求頭大小寫(xiě)不敏感,所以,我們可以使用任何大寫(xiě)。因此,我們可以使用任意大小寫(xiě)來(lái)訪問(wèn)請(qǐng)求頭:

>>> r.headers['Content-Type']
'application/json'
>>> r.headers.get('content-type')
'application/json'
Cookies

如果響應(yīng)包含Cookie,可以快速訪問(wèn)它們:

>>> url = 'http://example.com/some/cookie/setting/url'
>>> r = requests.get(url)
>>> r.cookies['example_cookie_name'] # 如果存在名為 example_cookie_name的cookie的話(huà)
'example_cookie_value'

可以使用cookies 參數(shù)將cookie發(fā)送給服務(wù)器:

>>> url = 'https://httpbin.org/cookies'
>>> cookies = dict(cookies_are='working')
>>> r = requests.get(url, cookies=cookies)
>>> r.text
'{\n  "cookies": {\n    "cookies_are": "working"\n  }\n}\n'

Cookies are returned in a RequestsCookieJar, which acts like a dict but also offers a more complete interface, suitable for use over multiple domains or paths. Cookie jars can also be passed in to requests:

返回的Cookie存儲(chǔ)在RequestsCookieJar中,其作用類(lèi)似于dict,同時(shí)提供了一個(gè)更完整的接口,適合在多個(gè)域或路徑上使用。Cookie jar也可以傳遞給請(qǐng)求:

>>> jar = requests.cookies.RequestsCookieJar()
>>> jar.set('tasty_cookie', 'yum', domain='httpbin.org', path='/cookies')
Cookie(version=0, name='tasty_cookie', value='yum', port=None, port_specified=False, domain='httpbin.org', domain_specified=True, domain_initial_dot=False, path='/cookies', path_specified=True, secure=False, expires=None, discard=True, comment=None, comment_url=None, rest={'HttpOnly': None}, rfc2109=False)
>>> jar.set('gross_cookie', 'blech', domain='httpbin.org', path='/elsewhere')
Cookie(version=0, name='gross_cookie', value='blech', port=None, port_specified=False, domain='httpbin.org', domain_specified=True, domain_initial_dot=False, path='/elsewhere', path_specified=True, secure=False, expires=None, discard=True, comment=None, comment_url=None, rest={'HttpOnly': None}, rfc2109=False)
>>> url = 'https://httpbin.org/cookies'
>>> r = requests.get(url, cookies=jar)
>>> r.text
'{"cookies": {"tasty_cookie": "yum"}}'
重定向與history

默認(rèn)情況下,requests將對(duì)除HEAD之外的所有請(qǐng)求執(zhí)行位置重定向(如果需要重定向的話(huà))。

我們可以使用Response對(duì)象的history屬性來(lái)跟蹤重定向。

Response.history列表包含為完成請(qǐng)求而創(chuàng)建的Response對(duì)象。列表按響應(yīng)的先后順序排序。

例如,Gitee將所有HTTP請(qǐng)求重定向到HTTPS:

>>> r = requests.get('http://gitee.com/')
>>> r.url
'https://gitee.com/'
>>> r.status_code
200
>>> r.history
[<Response [302]>]

如果使用HEAD,GET, OPTIONS, POST, PUT, PATCH 或者DELETE,可以使用 allow_redirects參數(shù)禁止重定向:

>>> r = requests.get('http://gitee.com/', allow_redirects=False)
>>> r.status_code
302
>>> r.history
[]
>>> r = requests.head('http://gitee.com/', allow_redirects=False)
>>> r.url
'http://gitee.com/'
>>> r.status_code
302
>>> r.history
[]
>>> r = requests.head('http://gitee.com/', allow_redirects=True)
>>> r.status_code
200
>>> r.url
'https://gitee.com/'
>>> r.history
[<Response [302]>]
請(qǐng)求超時(shí)

可以使用timeout參數(shù)告訴requests在給定的秒數(shù)后停止等待響應(yīng)。幾乎所有的生產(chǎn)代碼都應(yīng)該在幾乎所有的請(qǐng)求中使用此參數(shù)。否則會(huì)導(dǎo)致程序無(wú)限期掛起:

>>> requests.get('https://gitee.com/', timeout=0.1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  ...
urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='gitee.com', port=443): Read timed out. (read timeout=0.1)

注意:

timeout不是整個(gè)響應(yīng)的下載時(shí)間限制;相反,如果服務(wù)器在timeout秒內(nèi)沒(méi)有發(fā)出響應(yīng)(更準(zhǔn)確地說(shuō),如果在timeout秒內(nèi)底層socket沒(méi)有接收到任何字節(jié)數(shù)據(jù)),則會(huì)引發(fā)異常。如果未明確指定timeout,則請(qǐng)求不會(huì)超時(shí)。

錯(cuò)誤和異常

如果出現(xiàn)網(wǎng)絡(luò)問(wèn)題(例如DNS故障、拒絕連接等),requests將拋出ConnectionError異常。

如果HTTP請(qǐng)求返回了失敗的狀態(tài)代碼,Response.raise_for_statu()將拋出HTTPError

如果請(qǐng)求超時(shí),則會(huì)拋出Timeout異常。

如果請(qǐng)求超過(guò)了配置的最大重定向次數(shù),則會(huì)拋出TooManyRedirects異常。

requests顯式拋出的所有異常都繼承自requests.exceptions.RequestException

高級(jí)用法

Session對(duì)象

Session對(duì)象允許你跨請(qǐng)求保持某些參數(shù),以及Session實(shí)例發(fā)出的所有請(qǐng)求的cookie,并將使用urllib3的[連接池](https://urllib3.readthedocs.io/en/latest/reference/index.html#module-urllib3.connectionpool)。因此,如果你向同一主機(jī)發(fā)出多個(gè)請(qǐng)求,將復(fù)用底層TCP連接,這可能會(huì)顯著提高性能(請(qǐng)參見(jiàn)HTTP持久連接)。

Session對(duì)象具有主要 requests API的所有方法。

讓我們?cè)谡?qǐng)求之間保持一些cookie:

>>> s = requests.Session()
>>> s.get('https://httpbin.org/cookies/set/sessioncookie/123456789')
<Response [200]>
>>> r = s.get('https://httpbin.org/cookies')
>>> r.text
'{\n  "cookies": {\n    "sessioncookie": "123456789"\n  }\n}\n'
>>>

Seesion對(duì)象還可以用于向請(qǐng)求方法提供默認(rèn)數(shù)據(jù)。這是通過(guò)向Session對(duì)象屬性提供數(shù)據(jù)來(lái)實(shí)現(xiàn)的:

>>> s = requests.Session()
>>> s.auth = ('user', 'pass')
>>> s.headers.update({'x-test': 'true'})
# 'x-test'和'x-test2'請(qǐng)求頭隨請(qǐng)求發(fā)送了
>>> s.headers.update({'x-test': 'true'})
>>> s.get('https://httpbin.org/headers', headers={'x-test2': 'true'})
<Response [200]>

傳遞給請(qǐng)求方法的任何字典都將與會(huì)話(huà)級(jí)別設(shè)置的值合并。方法級(jí)別的參數(shù)會(huì)覆蓋會(huì)話(huà)級(jí)別的參數(shù)。

然而,請(qǐng)注意,即使使用會(huì)話(huà),方法級(jí)參數(shù)也不能跨請(qǐng)求保持。本示例將只在發(fā)送第一個(gè)請(qǐng)求發(fā)送cookie,而不發(fā)送第二個(gè)請(qǐng)求

>>> s = requests.Session()
>>> r = s.get('https://httpbin.org/cookies', cookies={'from-my': 'browser'})
>>> r.text
'{\n  "cookies": {\n    "from-my": "browser"\n  }\n}\n'
>>> r = s.get('https://httpbin.org/cookies')
>>> r.text
'{\n  "cookies": {}\n}\n'

Cookie utility functions to manipulate Session.cookies

如果想手動(dòng)向Session添加Cookie,那么使用 Cookie utility functions來(lái)操作Session.cookies

Session對(duì)象也可以用作上下文管理器

>>> with requests.Session() as s:
...     s.get('https://httpbin.org/cookies/set/sessioncookie/123456789')
...
<Response [200]>
>>>

這將確保在退出with塊后立即關(guān)閉會(huì)話(huà),即使發(fā)生未處理的異常。

Remove a Value From a Dict Parameter
Sometimes you'll want to omit session-level keys from a dict parameter. To do this, you simply set that key's value to `None` in the method-level parameter. It will automatically be omitted.
從字典參數(shù)中刪除值
有時(shí),你需要從dict參數(shù)中忽略會(huì)話(huà)級(jí)別的鍵。為此,只需在方法級(jí)參數(shù)中將該鍵的值設(shè)置為“None”即可。它將被自動(dòng)忽略。

Session中包含的所有值都可以直接使用。參見(jiàn)Session API Docs了解更多信息。

請(qǐng)求和響應(yīng)對(duì)象

示例:獲取響應(yīng)頭和請(qǐng)求頭

>>> r = s.get('https://httpbin.org')
>>> r.headers # 獲取響應(yīng)頭
{'Date': 'Mon, 13 Mar 2023 15:43:41 GMT', 'Content-Type': 'text/html; charset=utf-8', 'Content-Length': '9593', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true'}
>>> r.request.headers
{'User-Agent': 'python-requests/2.27.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Cookie': 'sessioncookie=123456789'}
>>>
Prepared requests

每當(dāng)收到來(lái)自某個(gè)API調(diào)用或者Session調(diào)用的Response對(duì)象,request屬性實(shí)際上是所使用的PreparedRequest。在某些情況下,你可能希望在發(fā)送請(qǐng)求之前對(duì)請(qǐng)求體或請(qǐng)求頭(或其他任何內(nèi)容)做一些額外的工作。簡(jiǎn)單的做法如下:

from requests import Request, Session
s = Session()
req = Request('POST', url, data=data, headers=headers)
prepped = req.prepare()
# do something with prepped.body
prepped.body = 'No, I want exactly this as the body.'
# do something with prepped.headers
del prepped.headers['Content-Type']
resp = s.send(prepped,
    stream=stream,
    verify=verify,
    proxies=proxies,
    cert=cert,
    timeout=timeout
)
print(resp.status_code)

However, the above code will lose some of the advantages of having a requests Session object. In particular, Session-level state such as cookies will not get applied to your request. To get a PreparedRequest with that state applied, replace the call to Request.prepare() with a call to Session.prepare_request(), like this:

由于你沒(méi)有對(duì)Request對(duì)象執(zhí)行任何特殊操作,因此您可以立即prepare它并修改PreparedRequest對(duì)象。然后將其與發(fā)送給requests.*Session.*的其它參數(shù)一起發(fā)送。

然而,上述代碼將失去使用requests Session對(duì)象的一些優(yōu)點(diǎn)。特別是Session級(jí)別的狀態(tài),比如cookie將不會(huì)應(yīng)用于你的請(qǐng)求。如果需要獲取應(yīng)用了那些狀態(tài)的 PreparedRequest,替換 Request.prepare() 調(diào)用為Session.prepare_request(),像這樣:

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
prepped.body = 'Seriously, send exactly these bytes.'

# do something with prepped.headers
prepped.headers['Keep-Dead'] = 'parrot'

resp = s.send(prepped,
    stream=stream,
    verify=verify,
    proxies=proxies,
    cert=cert,
    timeout=timeout
)

print(resp.status_code)

When you are using the prepared request flow, keep in mind that it does not take into account the environment. This can cause problems if you are using environment variables to change the behaviour of requests. For example: Self-signed SSL certificates specified in REQUESTS_CA_BUNDLE will not be taken into account. As a result an SSL: CERTIFICATE_VERIFY_FAILED is thrown. You can get around this behaviour by explicitly merging the environment settings into your session:

當(dāng)你使用prepared request請(qǐng)求時(shí),請(qǐng)記住它沒(méi)有考慮環(huán)境。如果你正使用環(huán)境變量來(lái)更改請(qǐng)求的行為,這可能會(huì)導(dǎo)致問(wèn)題。例如:在REQUESTS_CA_BUNDLE中指定的自簽名SSL證書(shū)將不起作用,結(jié)果引發(fā)了SSL:CERTIFICATE_VERIFY_FAILED。你可以通過(guò)將環(huán)境設(shè)置顯式合并到Session中來(lái)避免這種行為:

from requests import Request, Session
s = Session()
req = Request('GET', url)
prepped = s.prepare_request(req)
# Merge environment settings into session
settings = s.merge_environment_settings(prepped.url, {}, None, None, None)
resp = s.send(prepped, **settings)
print(resp.status_code)
HTTP Basic 驗(yàn)證
>>> from requests.auth import HTTPBasicAuth
>>> auth = HTTPBasicAuth('your_username', 'your_password')
>>> r = requests.post(url='you_target_url', data=body, auth=auth)
SSL證書(shū)驗(yàn)證

requests驗(yàn)證HTTPS請(qǐng)求的SSL證書(shū),就像web瀏覽器一樣。默認(rèn)情況下,SSL驗(yàn)證已啟用,如果無(wú)法驗(yàn)證證書(shū),請(qǐng)求將拋出SSLError:

>>> requests.get('https://requestb.in')
requests.exceptions.SSLError: hostname 'requestb.in' doesn't match either of '*.herokuapp.com', 'herokuapp.com'

你可以使用verify參數(shù)傳遞擁有受信任CA的證書(shū)的CA_BUNDLE文件的路徑或者目錄:

&gt;&gt;&gt; requests.get('https://github.com', verify='/path/to/certfile')

或者

s = requests.Session()
s.verify = '/path/to/certfile'

注意:

如果verify設(shè)置為目錄的路徑,則必須使用OpenSSL提供的c_rehash實(shí)用程序處理該目錄。

還可以通過(guò)REQUESTS_CA_BUNDLE環(huán)境變量指定此受信任CA列表。如果未設(shè)置REQUESTS_CA_BUNDLE,將使用CURL_CA_BUNDLE 。

如果將verify設(shè)置為False,則requests也可以忽略SSL證書(shū)驗(yàn)證:

>>> requests.get('https://kennethreitz.org', verify=False)
<Response [200]>

請(qǐng)注意,當(dāng)verify設(shè)置為False時(shí),Requests將接受服務(wù)器提供的任何TLS證書(shū),并將忽略主機(jī)名不匹配,或過(guò)期的證書(shū),這將使你的應(yīng)用程序容易受到中間人(MitM)攻擊。在本地開(kāi)發(fā)或測(cè)試期間,將verify設(shè)置為False可能很有用。

默認(rèn)情況下,verify設(shè)置為True。選項(xiàng)verify僅適用于主機(jī)證書(shū)。

客戶(hù)端證書(shū)

你還可以將本地證書(shū)指定為客戶(hù)端證書(shū)、單個(gè)文件(包含私鑰和證書(shū))或兩個(gè)文件路徑的元組

>>> requests.get('https://kennethreitz.org', cert=('/path/client.cert', '/path/client.key'))
<Response [200]>

或者:

s = requests.Session()
s.cert = '/path/client.cert'

警告

本地證書(shū)的私鑰必須為未加密的。當(dāng)前,Requests不支持加密的私鑰

CA證書(shū)

Reuests使用來(lái)自certific包中的證書(shū). 這允許用戶(hù)在不更改Requests版本的情況下更新其受信任的證書(shū)。

在2.16版本之前,Requests捆綁了一組其信任的根CA證書(shū),證書(shū)來(lái)源于Mzillatruststore。每個(gè)Request版本只更新一次證書(shū)。當(dāng)未安裝certific時(shí),當(dāng)使用較舊版本的requests時(shí),這會(huì)導(dǎo)致證書(shū)包非常過(guò)時(shí)。

為了安全起見(jiàn),我們建議經(jīng)常升級(jí)certific

Body內(nèi)容工作流

默認(rèn)情況下,當(dāng)你發(fā)出一個(gè)請(qǐng)求時(shí),將立即下載響應(yīng)的正文。你可以使用stream參數(shù)覆蓋此行為并延遲下載響應(yīng)主體直到訪問(wèn)response.content屬性

tarball_url = 'https://github.com/psf/requests/tarball/main'
r = requests.get(tarball_url, stream=True)

此時(shí),僅僅響應(yīng)頭被下載,且連接保持打開(kāi)狀態(tài),因此,允許我們有條件的檢索內(nèi)容:

if int(r.headers.get('content-length')) &lt; TOO_LONG:
  content = r.content
  ...

您可以使用 Response.iter_content()Response.iter_lines() 方法進(jìn)一步控制工作流。或者,可以從位于Response.raw的底層的urllib3.HTTPResponse 中讀取未編碼的主體.

如果在發(fā)出請(qǐng)求時(shí)將stream設(shè)置為True,則requests無(wú)法釋放連接回連接池,除非讀取完所有數(shù)據(jù)或調(diào)用Response.close。這可能導(dǎo)致連接效率低下。如果你發(fā)現(xiàn)自己在使用stream=True時(shí)部分讀取請(qǐng)求體(或根本沒(méi)有讀取它們),則應(yīng)在with語(yǔ)句中發(fā)出請(qǐng)求,以確保連接最終處于關(guān)閉狀態(tài):

with requests.get('https://httpbin.org/get', stream=True) as r:
    # Do things with the response here.
Keep-Alive

多虧了urllib3keep-alive在Session中是100%自動(dòng)的!你在Session發(fā)出的任何請(qǐng)求都將自動(dòng)重用合適的連接!

注意,只有在讀取了所有響應(yīng)體數(shù)據(jù)后,才會(huì)將連接釋放回連接池以供重用;請(qǐng)確保將stream設(shè)置為False或讀取Response對(duì)象的content屬性。

流式上傳

requests支持流式上傳,允許發(fā)送大型流或文件,而無(wú)需將其讀入內(nèi)存。要流式傳輸和上傳,只需為請(qǐng)求體提供一個(gè)類(lèi)似文件的對(duì)象:

with open('massive-body', 'rb') as f:
    requests.post('http://some.url/streamed', data=f)

警告

強(qiáng)烈建議以二進(jìn)制模式打開(kāi)文件。這是因?yàn)閞equests可能會(huì)嘗試為你提供Content-Length請(qǐng)求頭,如果這樣做,該請(qǐng)求頭值將被設(shè)置為文件中的字節(jié)數(shù)。如果以文本模式打開(kāi)文件,可能會(huì)發(fā)生錯(cuò)誤。

分塊編碼(Chunk-Encoded)請(qǐng)求

requests 還支持傳出和傳入請(qǐng)求的分塊傳輸編碼。要發(fā)送塊編碼請(qǐng)求,只需簡(jiǎn)單的為請(qǐng)求體提供一個(gè)生成器(或任何沒(méi)有長(zhǎng)度的迭代器)

def gen():
    yield 'hi'
    yield 'there'
requests.post('http://some.url/chunked', data=gen())

對(duì)于分塊編碼請(qǐng)求的響應(yīng),最好使用Response.iter_content()對(duì)數(shù)據(jù)進(jìn)行迭代。在理想情況下,將在請(qǐng)求上設(shè)置stream=True,在這種情況下,可以通過(guò)使用值為Nonechunk_size參數(shù)調(diào)用iter_content來(lái)逐塊迭代。如果要設(shè)置塊的最大大小,可以將chunk_size參數(shù)設(shè)置為任意目標(biāo)大小整數(shù)。

POST 多個(gè)分部編碼(Multipart-Encoded)文件

你可以在一個(gè)請(qǐng)求中發(fā)送多個(gè)文件。例如,假設(shè)你要將圖像文件上載到具有多個(gè)文件字段“images”的HTML表單:

<input type="file" name="images" multiple="true" required="true"/>

為此,只需將files設(shè)置為(form_field_name,file_info)的元組列表:

>>> url = 'https://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
>>> r.text
'{\n  "args": {}, \n  "data": "", \n  "files": {\n    "images": "data:image/png;base64,iVBORw0KGgoAAAAN...=="\n  }, \n  "form": {}, \n  "headers": {\n    "Accept": "*/*", \n    "Accept-Encoding": "gzip, deflate", \n    "Content-Length": "1800", \n    "Content-Type": "multipart/form-data; boundary=771ef90459071106c5f47075cbca2659", \n    "Host": "httpbin.org", \n    "User-Agent": "python-requests/2.27.1", \n    "X-Amzn-Trace-Id": "Root=1-641122ea-10a6271f0fdf488c70cf90e9"\n  }, \n  "json": null, \n  "origin": "183.62.127.25", \n  "url": "https://httpbin.org/post"\n}\n'
事件鉤子(Event Hooks)

requests擁有一個(gè)hook系統(tǒng),可用于控制請(qǐng)求過(guò)程的部分,或者信號(hào)事件處理。

可用的hooks:

  • response:

    請(qǐng)求生成的響應(yīng)

通過(guò)將{hook_name:callback_function}字典傳遞給hooks請(qǐng)求參數(shù),可以按每個(gè)請(qǐng)求分配一個(gè)鉤子函數(shù):

hooks={'response': print_url}

callback_function將接收一數(shù)據(jù)塊(a chunk of data)作為其第一個(gè)參數(shù)。

def print_url(r, *args, **kwargs):
    print(r.url)

回調(diào)函數(shù)必須處理其自己的異常。任何為處理的異常,都不會(huì)以靜默方式傳遞,因此應(yīng)該由代碼調(diào)用請(qǐng)求來(lái)處理。

如果回調(diào)函數(shù)返回某個(gè)值,則假定它將替換傳入的數(shù)據(jù)。如果函數(shù)不返回任何內(nèi)容,則不產(chǎn)生任何影響

def record_hook(r, *args, **kwargs):
    r.hook_called = True
    return r

讓我們?cè)谶\(yùn)行時(shí)打印一些請(qǐng)求方法參數(shù):

>>> requests.get('https://httpbin.org/', hooks={'response': print_url})
https://httpbin.org/
<Response [200]>

可以添加多個(gè)鉤子到單個(gè)請(qǐng)求中,如下,一次調(diào)用兩個(gè)鉤子函數(shù):

>>> r = requests.get('https://httpbin.org/', hooks={'response': [print_url, record_hook]})
>>> r.hook_called
True

還可以為Session實(shí)例添加鉤子,這樣添加的任何鉤子都將在向會(huì)話(huà)發(fā)出的每個(gè)請(qǐng)求中被調(diào)用。例如:

>>> s = requests.Session()
>>> s.hooks['response'].append(print_url)
>>> s.get('https://httpbin.org/')
 https://httpbin.org/
 <Response [200]>

如果Session實(shí)例可個(gè)鉤子函數(shù),那么將按鉤子的添加順序調(diào)用這些鉤子。

自定義身份驗(yàn)證

requests 請(qǐng)求支持自定義身份驗(yàn)證機(jī)制。

作為auth參數(shù)傳遞給請(qǐng)求方法的任何可調(diào)用對(duì)象都有機(jī)會(huì)在發(fā)送請(qǐng)求之前修改請(qǐng)求。

身份驗(yàn)證實(shí)現(xiàn)為AuthBase的子類(lèi),并且易于定義。requests在requests.auth中提供了兩種常見(jiàn)的身份驗(yàn)證方案實(shí)現(xiàn):HTTPBasicAuthHTTPDigestAuth.

假設(shè)我們有一個(gè)web服務(wù),它只有在X-Pizza請(qǐng)求頭設(shè)置為密碼值時(shí)才會(huì)響應(yīng)。這不太可能,暫且還是順著它:

from requests.auth import AuthBase
class PizzaAuth(AuthBase):
    """Attaches HTTP Pizza Authentication to the given Request object."""
    def __init__(self, username):
        # setup any auth-related data here
        self.username = username
    def __call__(self, r):
        # modify and return the request
        r.headers['X-Pizza'] = self.username
        return r

然后,發(fā)送請(qǐng)求

>>> requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth'))
<Response [200]>
流式請(qǐng)求

使用Response.iter_lines() ,可以很輕易的迭代流式API,比如 Twitter Streaming API。簡(jiǎn)單的設(shè)置 streamTrue 并且使用iter_lines對(duì)響應(yīng)進(jìn)行迭代:

import json
import requests
r = requests.get('https://httpbin.org/stream/20', stream=True)
for line in r.iter_lines():
    # filter out keep-alive new lines
    if line:
        decoded_line = line.decode('utf-8')
        print(json.loads(decoded_line))

decode_unicode=TrueResponse.iter_lines() 或者Response.iter_content()配合使用時(shí),如果服務(wù)器未提供編碼,則需要提供編碼:

r = requests.get('https://httpbin.org/stream/20', stream=True)
if r.encoding is None:
    r.encoding = 'utf-8'
for line in r.iter_lines(decode_unicode=True):
    if line:
        print(json.loads(line))

警告

iter_lines 不是可重入安全的。多次調(diào)用此方法會(huì)導(dǎo)致一些接收到的數(shù)據(jù)丟失。如果需要從多個(gè)地方調(diào)用它,請(qǐng)使用生成的迭代器對(duì)象:

lines = r.iter_lines()
# Save the first line for later or just skip it
first_line = next(lines)
for line in lines:
    print(line)
代理

如果你需要使用代理,可在任何請(qǐng)求方法的proxys參數(shù)中為單個(gè)請(qǐng)求配置代理

import requests
proxies = {
  'http': 'http://10.10.1.10:3128',
  'https': 'http://10.10.1.10:1080',
}
requests.get('http://example.org', proxies=proxies)

可選的,可以一次性為整個(gè)Session配置代理。

import requests
proxies = {
  'http': 'http://10.10.1.10:3128',
  'https': 'http://10.10.1.10:1080',
}
session = requests.Session()
session.proxies.update(proxies)
session.get('http://example.org')

警告

session.proxies提供的值可能被環(huán)境代理(由urllib.request.getproxys返回的值)覆蓋,所以為了確保在環(huán)境代理存在的情況下,也使用給定代理,顯示為所有單個(gè)請(qǐng)求指定proxies參數(shù),如上述一開(kāi)始所述。

如果沒(méi)有為請(qǐng)求設(shè)置proxies請(qǐng)求參數(shù)的情況下,requests會(huì)嘗試讀取由標(biāo)準(zhǔn)環(huán)境變量 http_proxy, https_proxy, no_proxyall_proxy定義的代理配置。這些變量名稱(chēng)可大寫(xiě)。所以,可以通過(guò)這些變量配置為請(qǐng)求設(shè)置代理(請(qǐng)根據(jù)實(shí)際需要配置):

linux:

$ export HTTP_PROXY="http://10.10.1.10:3128"
$ export HTTPS_PROXY="http://10.10.1.10:1080"
$ export ALL_PROXY="socks5://10.10.1.10:3434"
$ python
>>> import requests
>>> requests.get('http://example.org')

win:

set HTTP_PROXY=http://10.10.1.10:3128
>>> import requests
>>> requests.get('http://example.org')

要對(duì)代理使用HTTP基本身份驗(yàn)證,請(qǐng)?jiān)谏鲜鋈我獯砼渲萌肟谥惺褂?code>http://user:password@host/語(yǔ)法:

$ export HTTPS_PROXY="http://user:pass@10.10.1.10:1080"
$ python
>>> proxies = {'http': 'http://user:pass@10.10.1.10:3128/'}

警告

將敏感的用戶(hù)名和密碼信息存儲(chǔ)在環(huán)境變量或版本控制的文件中會(huì)帶來(lái)安全風(fēng)險(xiǎn),強(qiáng)烈建議不要這樣做。

如果要為特定shema和主機(jī)提供代理,請(qǐng)使用scheme://hostnameproxies字典參數(shù)的鍵來(lái)設(shè)置代理。這將匹配給定scheme和確切主機(jī)名的任何請(qǐng)求。

proxies = {'http://10.20.1.128': 'http://10.10.1.10:5323'}

注意,代理URL必須包含schema。

最后需要注意的,為https連接設(shè)置代理,通常需要所在本機(jī)機(jī)器信任代理根證書(shū)。默認(rèn)的,可以通過(guò)以下代碼查找requests信任的證書(shū)列表:

from requests.utils import DEFAULT_CA_BUNDLE_PATH
print(DEFAULT_CA_BUNDLE_PATH)

通過(guò)將 REQUESTS_CA_BUNDLE (or CURL_CA_BUNDLE) 環(huán)境變量設(shè)置為另一個(gè)文件路徑,可以覆蓋此證書(shū)路徑:

$ export REQUESTS_CA_BUNDLE="/usr/local/myproxy_info/cacert.pem"
$ export https_proxy="http://10.10.1.10:1080"
$ python
>>> import requests
>>> requests.get('https://example.org')
SOCKS

版本2.10.0中新增

除了基本的HTTP代理之外,requests還支持使用SOCKS協(xié)議的代理。這是一項(xiàng)可選功能,要求在使用前安裝其他第三方庫(kù)

可通過(guò)pip獲取該功能需要的依賴(lài):

$ python -m pip install requests[socks]

安裝依賴(lài)后,使用SOCKS代理就同使用HTTP代理一樣簡(jiǎn)單:

proxies = {
    'http': 'socks5://user:pass@host:port',
    'https': 'socks5://user:pass@host:port'
}

使用 socks5 會(huì)導(dǎo)致DNS解析發(fā)生在客戶(hù)端上,而不是代理服務(wù)器上。這與curl保持一致,curl使用scheme來(lái)決定是在客戶(hù)端還是代理服務(wù)器上進(jìn)行DNS解析。如果要解析代理服務(wù)器上的域,請(qǐng)使用socks5h作為scheme

編碼

當(dāng)收到響應(yīng)時(shí),并訪問(wèn) Response.text屬性時(shí),requests會(huì)猜測(cè)用于解碼響應(yīng)體的編碼。requests將首先檢查HTTP請(qǐng)求頭中的編碼,如果不存在,則使用charset_normalizer或chardet嘗試猜測(cè)編碼。

如果安裝了chardet,requests將使用它,但對(duì)于python3來(lái)說(shuō),chardet不再是強(qiáng)制依賴(lài)項(xiàng)。

當(dāng)安裝requests時(shí),沒(méi)有指定 [use_chardet_on_py3],并且chardet尚未安裝時(shí),requests將使用charset normalizer來(lái)猜測(cè)編碼。

requests不會(huì)猜測(cè)編碼的唯一情況是HTTP請(qǐng)求頭中不存在顯示字符集且Content-Type請(qǐng)求頭包含text。在這種情況下,RFC 2616指定默認(rèn)字符集必須是ISO-8859-1。requests遵循該規(guī)范。如果需要不同的編碼,您可以手動(dòng)設(shè)置Response.conding屬性,或使用原始Response.content

Link請(qǐng)求頭

許多HTTP API具有l(wèi)ink請(qǐng)求頭。它們使API更加自我描述和可發(fā)現(xiàn)。

GitHub 在API中將這些用于分頁(yè)

>>> 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/user/119893/repos?page=2&per_page=10>; rel="next", <https://api.github.com/user/119893/repos?page=5&per_page=10>; rel="last"'

requests 將自動(dòng)解析這link請(qǐng)求頭并且讓它們更容易被使用:

>>> r.links["next"]
{'url': 'https://api.github.com/user/119893/repos?page=2&per_page=10', 'rel': 'next'}
>>> r.links["last"]
{'url': 'https://api.github.com/user/119893/repos?page=5&per_page=10', 'rel': 'last'}
傳輸適配器(Transport Adapters)

從v1.0.0開(kāi)始,requests 已模塊化內(nèi)部設(shè)計(jì)。這樣做的部分原因是為了實(shí)現(xiàn)傳輸適配器,最初在此處描述. 傳輸適配器提供了一種機(jī)制來(lái)定義某個(gè)HTTP服務(wù)的交互方法。特別是,它們?cè)试S你應(yīng)用每個(gè)服務(wù)的配置。

requests附帶單個(gè)傳輸適配器HTTPAdapter. 此適配器使用功能強(qiáng)大的urllib3提供與HTTP和HTTPS的默認(rèn)請(qǐng)求交互。當(dāng)初始化 requests Session 時(shí),其中一個(gè)附加到Session 對(duì)象表示HTTP,一個(gè)表示HTTPS。

戶(hù)能夠創(chuàng)建和使用自己的具備特定功能的傳輸適配器。一旦創(chuàng)建,傳輸適配器就可以加載到會(huì)話(huà)對(duì)象,并指示它應(yīng)該應(yīng)用于哪些web服務(wù)。

>>> s = requests.Session()
>>> s.mount('https://github.com/', MyAdapter())

上述mount調(diào)用將傳輸適配器的指定實(shí)例注冊(cè)到URL前綴中。一旦掛載,使用該session發(fā)起的,URL以給定前綴開(kāi)頭的任何HTTP請(qǐng)求都將使用給定的傳輸適配器。

實(shí)現(xiàn)傳輸適配器的許多細(xì)節(jié)超出了本文檔的范圍,但是可以看下一個(gè)簡(jiǎn)單SSL使用示例。除此之外,您還可以考慮繼承BaseAdapter實(shí)現(xiàn)子類(lèi)適配器。

示例: 指定SSL版本

The requests team has made a specific choice to use whatever SSL version is default in the underlying library (urllib3). Normally this is fine, but from time to time, you might find yourself needing to connect to a service-endpoint that uses a version that isn&rsquo;t compatible with the default.

You can use Transport Adapters for this by taking most of the existing implementation of HTTPAdapter, and adding a parameter ssl_version that gets passed-through to urllib3. We&rsquo;ll make a Transport Adapter that instructs the library to use SSLv3:

默認(rèn)情況下,requests選擇使用底層urllib3庫(kù)中默認(rèn)的SSL版本。 通常情況下,這是可以的,但有時(shí),您可能會(huì)發(fā)現(xiàn)自己需要連接到使用與默認(rèn)版本不兼容的SSL版本的服務(wù)端。

為此,可以通過(guò)繼承HTTPAdapter實(shí)現(xiàn)自定義傳輸適配器,

示例:編寫(xiě)一個(gè)適配器,指示庫(kù)使用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)
阻塞或非阻塞

有了默認(rèn)的傳輸適配器,requests就不會(huì)提供任何類(lèi)型的非阻塞IO。Response.content屬性將阻塞,直到下載完整個(gè)響應(yīng)為止。如果你需要更大的粒度,則庫(kù)的流式傳輸功能(請(qǐng)參閱流式傳輸請(qǐng)求)允許單次接收較小數(shù)量的響應(yīng)那日。然而,這些調(diào)用仍然是阻塞。

如果您關(guān)心阻塞IO的使用,那么有很多項(xiàng)目將請(qǐng)求與Python的異步框架結(jié)合在一起。一些很好的例子是 requests-threads, grequests, requests-futures, 和httpx.

超時(shí)

大多數(shù)對(duì)外部服務(wù)器的請(qǐng)求都應(yīng)該附加超時(shí),以防服務(wù)器沒(méi)有及時(shí)響應(yīng)。默認(rèn)情況下,除非顯式設(shè)置了超時(shí)時(shí)間,否則requests不會(huì)超時(shí)。如果沒(méi)有超時(shí),你的代碼可能會(huì)掛起幾分鐘或更長(zhǎng)時(shí)間。

連接超時(shí)是requests等待客戶(hù)端建立與遠(yuǎn)程計(jì)算機(jī)的socke連接的秒數(shù)。將連接超時(shí)設(shè)置為略大于3的倍數(shù)是一種很好的做法,因?yàn)?秒是默認(rèn)的TCP數(shù)據(jù)包重傳窗口.

一旦客戶(hù)端連接到服務(wù)器并發(fā)送HTTP請(qǐng)求后,讀取超時(shí)是客戶(hù)端等待服務(wù)器返回響應(yīng)的秒數(shù)(具體來(lái)說(shuō),這是客戶(hù)端等待服務(wù)器返回字節(jié)數(shù)據(jù)的秒數(shù)。在99.9%的情況下,這是服務(wù)器返回第一個(gè)字節(jié)之前的等待時(shí)間)。

如果需要為請(qǐng)求設(shè)置一個(gè)超時(shí)時(shí)間,可以為timeout參數(shù)指定一個(gè)具體的時(shí)間值:

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

該超時(shí)時(shí)間將同時(shí)應(yīng)用于連接超時(shí)和讀取超時(shí)。如果想為連接超時(shí)和讀取超時(shí)分別設(shè)置不同的等待時(shí)間,可以指定一個(gè)元組:

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

如果服務(wù)很慢,想讓requests一直等待響應(yīng)直到獲取響應(yīng),可以指定timeout參數(shù)值為None

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

讀到這里,這篇“怎么使用Python的Requests庫(kù)”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過(guò)才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI