Python Requests庫使用指南
本文為譯文,原文鏈接 python-requests-library-guide
本人博客: 編程禪師
requests 庫是用來在Python中發出標準的HTTP請求。 它將請求背後的複雜性抽象成一個漂亮,簡單的API,以便你可以專註於與服務交互和在應用程序中使用數據。
在本文中,你將看到 requests
提供的一些有用的功能,以及如何針對你可能遇到的不同情況來自定義和優化這些功能。 你還將學習如何有效的使用 requests
,以及如何防止對外部服務的請求導致減慢應用程序的速度。
在本教程中,你將學習如何:
- 使用常見的HTTP方法發送請求
- 定製你的請求頭和數據,使用查詢字元串和消息體
- 檢查你的請求和響應的數據
- 發送帶身份驗證的請求
- 配置你的請求來避免阻塞或減慢你的應用程序
雖然我試圖包含儘可能多的信息來理解本文中包含的功能和示例,但閱讀此文需要對HTTP有基礎的了解。
現在讓我們深入了解如何在你的應用程序中使用請求!
開始使用 requests
讓我們首先安裝 requests
庫。 為此,請運行以下命令:
pip install requests
如果你喜歡使用 Pipenv 管理Python包,你可以運行下面的命令:
pipenv install requests
一旦安裝了 requests
,你就可以在應用程序中使用它。像這樣導入 requests
:
import requests
現在你已經都準備完成了,那麼是時候開始使用 requests
的旅程了。 你的第一個目標是學習如何發出GET請求。
GET 請求
HTTP方法(如GET和POST)決定當發出HTTP請求時嘗試執行的操作。 除了GET和POST之外,還有其他一些常用的方法,你將在本教程的後面部分使用到。
最常見的HTTP方法之一是GET。 GET方法表示你正在嘗試從指定資源獲取或檢索數據。 要發送GET請求,請調用 requests.get()
。
你可以通過下面方式來向GitHub的 Root REST API 發出GET請求:
>>> requests.get(https://api.github.com)
<Response [200]>
恭喜! 你發出了你的第一個請求。 接下來讓我們更深入地了解該請求的響應。
響應
Response
是檢查請求結果的強有力的對象。 讓我們再次發出相同的請求,但這次將返回值存儲在一個變數中,以便你可以仔細查看其屬性和方法:
>>> response = requests.get(https://api.github.com)
在此示例中,你捕獲了 get()
的返回值,該值是 Response
的實例,並將其存儲在名為 response
的變數中。 你現在可以使用 response
來查看有關GET請求結果的全部信息。
狀態碼
您可以從 Response
獲取的第一部分信息是狀態碼。 狀態碼會展示你請求的狀態。
例如,200 OK
狀態表示你的請求成功,而 404 NOT FOUND
狀態表示找不到你要查找的資源。 還有許多其它的狀態碼 ,可以為你提供關於你的請求所發生的具體情況。
通過訪問 .status_code
,你可以看到伺服器返回的狀態碼:
>>> response.status_code
200
.status_code
返回 200
意味著你的請求是成功的,並且伺服器返回你要請求的數據。
有時,你可能想要在代碼中使用這些信息來做判斷:
if response.status_code == 200:
print(Success!)
elif response.status_code == 404:
print(Not Found.)
按照這個邏輯,如果伺服器返回 200
狀態碼,你的程序將列印 Success!
如果結果是 404
,你的程序將列印 Not Found.
。
requests
更進一步為你簡化了此過程。 如果在條件表達式中使用 Response
實例,則在狀態碼介於 200
和 400
之間時將被計算為為 True
,否則為 False
。
因此,你可以通過重寫 if
語句來簡化上一個示例:
if response:
print(Success!)
else:
print(An error has occurred.)
技術細節 : 因為
這意味著重新定義了__ bool __()
是Response
上的重載方法 ,因此真值測試才成立。Response
的默認行為,用來在確定對象的真值時考慮狀態碼。
請記住,此方法 不會驗證
狀態碼是否等於 200
。原因是 200
到 400
範圍內的其他狀態代碼,例如 204 NO CONTENT
和 304 NOT MODIFIED
,就意義而言也被認為是成功的響應。
例如,204
告訴你響應是成功的,但是下消息體中沒有返回任何內容。
因此,通常如果你想知道請求是否成功時,請確保使用這方便的簡寫,然後在必要時根據狀態碼適當地處理響應。
假設你不想在 if
語句中檢查響應的狀態碼。 相反,如果請求不成功,你希望拋出一個異常。 你可以使用 .raise_for_status()
執行此操作:
import requests
from requests.exceptions import HTTPError
for url in [https://api.github.com, https://api.github.com/invalid]:
try:
response = requests.get(url)
# If the response was successful, no Exception will be raised
response.raise_for_status()
except HTTPError as http_err:
print(fHTTP error occurred: {http_err}) # Python 3.6
except Exception as err:
print(fOther error occurred: {err}) # Python 3.6
else:
print(Success!)
如果你調用 .raise_for_status()
,將針對某些狀態碼引發 HTTPError
異常。 如果狀態碼指示請求成功,則程序將繼續進行而不會引發該異常。
進一步閱讀:如果你不熟悉Python 3.6的 f-strings,我建議你使用它們,因為它們是簡化格式化字元串的好方法。
現在,你對於如何處理從伺服器返回的響應的狀態碼了解了許多。 但是,當你發出GET請求時,你很少只關心響應的狀態碼。 通常,你希望看到更多。 接下來,你將看到如何查看伺服器在響應正文中返回的實際數據。
響應內容
GET
請求的響應通常在消息體中具有一些有價值的信息,稱為有效負載。 使用 Response
的屬性和方法,你可以以各種不同的格式查看有效負載。
要以 位元組 格式查看響應的內容,你可以使用 .content
:
>>> response = requests.get(https://api.github.com)
>>> response.content
b{"current_user_url":"https://api.github.com/user","current_user_authorizations_html_url":"https://github.com/settings/connections/applications{/client_id}","authorizations_url":"https://api.github.com/authorizations","code_search_url":"https://api.github.com/search/code?q={query}{&page,per_page,sort,order}","commit_search_url":"https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}","emails_url":"https://api.github.com/user/emails","emojis_url":"https://api.github.com/emojis","events_url":"https://api.github.com/events","feeds_url":"https://api.github.com/feeds","followers_url":"https://api.github.com/user/followers","following_url":"https://api.github.com/user/following{/target}","gists_url":"https://api.github.com/gists{/gist_id}","hub_url":"https://api.github.com/hub","issue_search_url":"https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}","issues_url":"https://api.github.com/issues","keys_url":"https://api.github.com/user/keys","notifications_url":"https://api.github.com/notifications","organization_repositories_url":"https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}","organization_url":"https://api.github.com/orgs/{org}","public_gists_url":"https://api.github.com/gists/public","rate_limit_url":"https://api.github.com/rate_limit","repository_url":"https://api.github.com/repos/{owner}/{repo}","repository_search_url":"https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}","current_user_repositories_url":"https://api.github.com/user/repos{?type,page,per_page,sort}","starred_url":"https://api.github.com/user/starred{/owner}{/repo}","starred_gists_url":"https://api.github.com/gists/starred","team_url":"https://api.github.com/teams","user_url":"https://api.github.com/users/{user}","user_organizations_url":"https://api.github.com/user/orgs","user_repositories_url":"https://api.github.com/users/{user}/repos{?type,page,per_page,sort}","user_search_url":"https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"}
雖然 .content
允許你訪問響應有效負載的原始位元組,但你通常希望使用 UTF-8 等字元編碼將它們轉換為字元串。 當你訪問 .text
時,response
將為你執行此操作:
>>> response.text
{"current_user_url":"https://api.github.com/user","current_user_authorizations_html_url":"https://github.com/settings/connections/applications{/client_id}","authorizations_url":"https://api.github.com/authorizations","code_search_url":"https://api.github.com/search/code?q={query}{&page,per_page,sort,order}"...}"}
因為對 bytes
解碼到 str
需要一個編碼格式,所以如果你沒有指定,請求將嘗試根據響應頭來猜測編碼格式。 你也可以在訪問 .text
之前通過 .encoding
來顯式設置編碼:
>>> response.encoding = utf-8 # Optional: requests infers this internally
>>> response.text
{"current_user_url":"https://api.github.com/user","current_user_authorizations_html_url":"https://github.com/settings/connections/applications{/client_id}","authorizations_url":"https://api.github.com/authorizations","code_search_url":"https://api.github.com/search/code?q={query}{&page,per_page,sort,order}"...}"}
如果你看看響應,你會發現它實際上是序列化的 JSON
內容。 要獲取字典內容,你可以使用 .text
獲取 str
並使用json.loads()
對其進行反序列化。 但是,完成此任務的更簡單方法是使用 .json()
:
>>> response.json()
{current_user_url: https://api.github.com/user, current_user_authorizations_html_url: https://github.com/settings/connections/applications{/client_id}...}}
.json()
返回值的類型是字典類型,因此你可以使用鍵值對的方式訪問對象中的值。
你可以使用狀態碼和消息體做許多事情。 但是,如果你需要更多信息,例如有關 response
本身的元數據,則需要查看響應頭部。
響應頭部
響應頭部可以為你提供有用的信息,例如響應有效負載的內容類型以及緩存響應的時間限制。 要查看這些頭部,請訪問 .headers
:
>>> response.headers
{Server: GitHub.com, Date: Mon, 10 Dec 2018 17:49:54 GMT, Content-Type: application/json; charset=utf-8,...}
.headers
返回類似字典的對象,允許你使用鍵來獲取頭部中的值。 例如,要查看響應有效負載的內容類型,你可以訪問 Content-Type
:
>>> response.headers[Content-Type]
application/json; charset=utf-8
但是,這個類似於字典的頭部對象有一些特別之處。 HTTP規範定義頭部不區分大小寫,這意味著我們可以訪問這些頭信息而不必擔心它們的大小寫:
>>> response.headers[content-type]
application/json; charset=utf-8
無論您使用 content-type
還是 Content-Type
,你都將獲得相同的值。
現在,你已經學習了有關 Response
的基礎知識。 你已經看到了它最有用的屬性和方法。 讓我們退後一步,看看自定義 GET
請求時你的響應如何變化。
查詢字元串參數
自定義 GET
請求的一種常用方法是通過URL中的 查詢字元串 參數傳遞值。 要使用 get()
執行此操作,請將數據傳遞給 params
。 例如,你可以使用GitHub的Search API來查找 requests
庫:
import requests
# Search GitHubs repositories for requests
response = requests.get(
https://api.github.com/search/repositories,
params={q: requests+language:python},
)
# Inspect some attributes of the `requests` repository
json_response = response.json()
repository = json_response[items][0]
print(fRepository name: {repository["name"]}) # Python 3.6+
print(fRepository description: {repository["description"]}) # Python 3.6+
通過將字典 {q:requests + language:python}
傳遞給 .get()
的 params
參數,你可以修改從Search API返回的結果。
你可以像你剛才那樣以字典的形式或以元組列表形式將 params
傳遞給 get()
:
>>> requests.get(
... https://api.github.com/search/repositories,
... params=[(q, requests+language:python)],
... )
<Response [200]>
你甚至可以傳 bytes
作為值:
>>> requests.get(
... https://api.github.com/search/repositories,
... params=bq=requests+language:python,
... )
<Response [200]>
查詢字元串對於參數化GET請求很有用。 你還可以通過添加或修改發送的請求的頭部來自定義你的請求。
請求頭
要自定義請求頭,你可以使用 headers
參數將HTTP頭部組成的字典傳遞給 get()
。 例如,你可以通過 Accept
中指定文本匹配媒體類型來更改以前的搜索請求,以在結果中突出顯示匹配的搜索字詞:
import requests
response = requests.get(
https://api.github.com/search/repositories,
params={q: requests+language:python},
headers={Accept: application/vnd.github.v3.text-match+json},
)
# View the new `text-matches` array which provides information
# about your search term within the results
json_response = response.json()
repository = json_response[items][0]
print(fText matches: {repository["text_matches"]})
Accept
告訴伺服器你的應用程序可以處理哪些內容類型。 由於你希望突出顯示匹配的搜索詞,所以使用的是 application / vnd.github.v3.text-match + json
,這是一個專有的GitHub的 Accept
標頭,其內容為特殊的JSON格式。
在你了解更多自定義請求的方法之前,讓我們通過探索其他HTTP方法來拓寬視野。
其他HTTP方法
除了 GET
之外,其他流行的HTTP方法包括 POST
,PUT
,DELETE
,HEAD
,PATCH
和OPTIONS
。 requests
為每個HTTP方法提供了一個方法,與 get()
`具有類似的結構:
>>> requests.post(https://httpbin.org/post, data={key:value})
>>> requests.put(https://httpbin.org/put, data={key:value})
>>> requests.delete(https://httpbin.org/delete)
>>> requests.head(https://httpbin.org/get)
>>> requests.patch(https://httpbin.org/patch, data={key:value})
>>> requests.options(https://httpbin.org/get)
調用每個函數使用相應的HTTP方法向httpbin服務發出請求。 對於每種方法,你可以像以前一樣查看其響應:
>>> response = requests.head(https://httpbin.org/get)
>>> response.headers[Content-Type]
application/json
>>> response = requests.delete(https://httpbin.org/delete)
>>> json_response = response.json()
>>> json_response[args]
{}
每種方法的響應中都會返回頭部,響應正文,狀態碼等。 接下來,你將進一步了解 POST
,PUT
和 PATCH
方法,並了解它們與其他請求類型的區別。
消息體
根據HTTP規範,POST
,PUT
和不太常見的PATCH
請求通過消息體而不是通過查詢字元串參數傳遞它們的數據。 使用 requests
,你將有效負載傳遞給相應函數的 data
參數。
data
接收字典,元組列表,位元組或類文件對象。 你需要將在請求正文中發送的數據調整為與你交互的服務的特定格式。
例如,如果你的請求的內容類型是 application / x-www-form-urlencoded
,則可以將表單數據作為字典發送:
>>> requests.post(https://httpbin.org/post, data={key:value})
<Response [200]>
你還可以將相同的數據作為元組列表發送:
>>> requests.post(https://httpbin.org/post, data=[(key, value)])
<Response [200]>
但是,如果需要發送JSON數據,則可以使用 json
參數。 當你通過 json
傳遞JSON數據時,requests
將序列化你的數據並為你添加正確的 Content-Type
標頭。
httpbin.org 是 requests
作者 Kenneth Reitz 創建的一個很好的資源。 它是一種接收測試請求並響應有關請求數據的服務。 例如,你可以使用它來檢查基本的POST請求:
>>> response = requests.post(https://httpbin.org/post, json={key:value})
>>> json_response = response.json()
>>> json_response[data]
{"key": "value"}
>>> json_response[headers][Content-Type]
application/json
你可以從響應中看到伺服器在你發送請求時收到了請求數據和標頭。 requests
還以 PreparedRequest
的形式向你提供此信息。
檢查你的請求
當你發出請求時,requests
庫會在將請求實際發送到目標伺服器之前準備該請求。 請求準備包括像驗證頭信息和序列化JSON內容等。
你可以通過訪問 .request
來查看 PreparedRequest
:
>>> response = requests.post(https://httpbin.org/post, json={key:value})
>>> response.request.headers[Content-Type]
application/json
>>> response.request.url
https://httpbin.org/post
>>> response.request.body
b{"key": "value"}
通過檢查 PreparedRequest
,你可以訪問有關正在進行的請求的各種信息,例如有效負載,URL,頭信息,身份驗證等。
到目前為止,你已經發送了許多不同類型的請求,但它們都有一個共同點:它們是對公共API的未經身份驗證的請求。 你遇到的許多服務可能都希望你以某種方式進行身份驗證。
身份驗證
身份驗證可幫助服務了解你的身份。 通常,你通過將數據傳遞到 Authorization
頭信息或服務定義的自定義頭信息來向伺服器提供憑據。 你在此處看到的所有請求函數都提供了一個名為 auth
的參數,允許你傳遞憑據。
需要身份驗證的一個示例API的是GitHub的 Authenticated User API。 此端點提供有關經過身份驗證的用戶配置文件的信息。 要向 Authenticated User API
發出請求,你可以將你的GitHub的用戶名和密碼以元組傳遞給 get()
:
>>> from getpass import getpass
>>> requests.get(https://api.github.com/user, auth=(username, getpass()))
<Response [200]>
如果你在元組中傳遞給 auth
的憑據有效,則請求成功。 如果你嘗試在沒有憑據的情況下發出此請求,你將看到狀態代碼為 401 Unauthorized
:
>>> requests.get(https://api.github.com/user)
<Response [401]>
當你以元組形式吧用戶名和密碼傳遞給 auth
參數時,rqeuests
將使用HTTP的基本訪問認證方案來應用憑據。
因此,你可以通過使用 HTTPBasicAuth
傳遞顯式的基本身份驗證憑據來發出相同的請求:
>>> from requests.auth import HTTPBasicAuth
>>> from getpass import getpass
>>> requests.get(
... https://api.github.com/user,
... auth=HTTPBasicAuth(username, getpass())
... )
<Response [200]>
雖然你不需要明確進行基本身份驗證,但你可能希望使用其他方法進行身份驗證。 requests
提供了開箱即用的其他身份驗證方法,例如 HTTPDigestAuth
和 HTTPProxyAuth
。
你甚至可以提供自己的身份驗證機制。 為此,你必須首先創建AuthBase的子類。 然後,實現__call __()
:
import requests
from requests.auth import AuthBase
class TokenAuth(AuthBase):
"""Implements a custom authentication scheme."""
def __init__(self, token):
self.token = token
def __call__(self, r):
"""Attach an API token to a custom auth header."""
r.headers[X-TokenAuth] = f{self.token} # Python 3.6+
return r
requests.get(https://httpbin.org/get, auth=TokenAuth(12345abcde-token))
在這裡,你自定義的 TokenAuth
接收一個令牌,然後在你的請求頭中的 X-TokenAuth
頭中包含該令牌。
錯誤的身份驗證機制可能會導致安全漏洞,因此,除非服務因某種原因需要自定義身份驗證機制,否則你始終希望使用像 Basic
或 OAuth
這樣經過驗證的身份驗證方案。
在考慮安全性時,讓我們考慮使用 requests
處理SSL證書。
SSL證書驗證
每當你嘗試發送或接收的數據都很敏感時,安全性就很重要。 通過HTTP與站點安全通信的方式是使用SSL建立加密連接,這意味著驗證目標伺服器的SSL證書至關重要。
好消息是 requests
默認為你執行此操作。 但是,在某些情況下,你可能希望更改此行為。
如果要禁用SSL證書驗證,請將 False
傳遞給請求函數的 verify
參數:
>>> requests.get(https://api.github.com, verify=False)
InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
InsecureRequestWarning)
<Response [200]>
當你提出不安全的請求時,requests
甚至會發出警告來幫助你保護數據安全。
性能
在使用 requests
時,尤其是在生產應用程序環境中,考慮性能影響非常重要。 超時控制,會話和重試限制等功能可以幫助你保持應用程序平穩運行。
超時控制
當你向外部服務發出請求時,系統將需要等待響應才能繼續。 如果你的應用程序等待響應的時間太長,則可能會阻塞對你的服務的請求,你的用戶體驗可能會受到影響,或者你的後台作業可能會掛起。
默認情況下,requests
將無限期地等待響應,因此你幾乎應始終指定超時時間以防止這些事情發生。 要設置請求的超時,請使用 timeout
參數。 timeout
可以是一個整數或浮點數,表示在超時之前等待響應的秒數:
>>> requests.get(https://api.github.com, timeout=1)
<Response [200]>
>>> requests.get(https://api.github.com, timeout=3.05)
<Response [200]>
在第一個請求中,請求將在1秒後超時。 在第二個請求中,請求將在3.05秒後超時。
你還可以將元組傳遞給 timeout
,第一個元素是連接超時(它允許客戶端與伺服器建立連接的時間),第二個元素是讀取超時(一旦你的客戶已建立連接而等待響應的時間):
>>> requests.get(https://api.github.com, timeout=(2, 5))
<Response [200]>
如果請求在2秒內建立連接並在建立連接的5秒內接收數據,則響應將按原樣返回。 如果請求超時,則該函數將拋出 Timeout
異常:
import requests
from requests.exceptions import Timeout
try:
response = requests.get(https://api.github.com, timeout=1)
except Timeout:
print(The request timed out)
else:
print(The request did not time out)
你的程序可以捕獲 Timeout
異常並做出相應的響應。
Session對象
到目前為止,你一直在處理高級請求API,例如 get()
和 post()
。 這些函數是你發出請求時所發生的事情的抽象。 為了你不必擔心它們,它們隱藏了實現細節,例如如何管理連接。
在這些抽象之下是一個名為 Session
的類。 如果你需要微調對請求的控制方式或提高請求的性能,則可能需要直接使用 Session
實例。
Session
用於跨請求保留參數。 例如,如果要跨多個請求使用相同的身份驗證,則可以使用session
:
import requests
from getpass import getpass
# By using a context manager, you can ensure the resources used by
# the session will be released after use
with requests.Session() as session:
session.auth = (username, getpass())
# Instead of requests.get(), youll use session.get()
response = session.get(https://api.github.com/user)
# You can inspect the response just like you did before
print(response.headers)
print(response.json())
每次使用 session
發出請求時,一旦使用身份驗證憑據初始化,憑據將被保留。
session
的主要性能優化以持久連接的形式出現。 當你的應用程序使用 Session
建立與伺服器的連接時,它會在連接池中保持該連接。 當你的應用程序想要再次連接到同一伺服器時,它將重用池中的連接而不是建立新連接。
最大重試
請求失敗時,你可能希望應用程序重試相同的請求。 但是,默認情況下,requests
不會為你執行此操作。 要應用此功能,您需要實現自定義 Transport Adapter。
通過 Transport Adapters
,你可以為每個與之交互的服務定義一組配置。 例如,假設你希望所有對於https://api.github.com的請求在最終拋出 ConnectionError
之前重試三次。 你將構建一個 Transport Adapter
,設置其 max_retries
參數,並將其裝載到現有的 Session
:
import requests
from requests.adapters import HTTPAdapter
from requests.exceptions import ConnectionError
github_adapter = HTTPAdapter(max_retries=3)
session = requests.Session()
# Use `github_adapter` for all requests to endpoints that start with this URL
session.mount(https://api.github.com, github_adapter)
try:
session.get(https://api.github.com)
except ConnectionError as ce:
print(ce)
當您將 HTTPAdapter(github_adapter)
掛載到 session
時,session
將遵循其對https://api.github.com的每個請求的配置。
Timeouts
,Transport Adapters
和 Sessions
用於保持代碼高效和應用程序的魯棒性。
總結
在學習Python中強大的 requests
庫方面,你已經走了很長的路。
你現在能夠:
- 使用各種不同的HTTP方法發出請求,例如GET,POST和PUT
- 通過修改請求頭,身份驗證,查詢字元串和消息體來自定義你的請求
- 檢查發送到伺服器的數據以及伺服器發回給你的數據
- 使用SSL證書驗證
- 高效的使用
requests
通過使用max_retries
,timeout
,Sessions
和Transport Adapters
因為您學會了如何使用 requests
,所以你可以使用他們提供的迷人數據探索廣泛的Web服務世界並構建出色的應用程序了。
TAG:Python |