Pythonのリクエストライブラリ(ガイド)
requests
ライブラリは、PythonでHTTPリクエストを行うためのデファクトスタンダードです。 これは、美しくシンプルなAPIの背後にあるリクエストの複雑さを抽象化し、サービスとのやり取りやアプリケーションでのデータの消費に集中できるようにします。
この記事全体を通して、requests
が提供する必要のある最も便利な機能のいくつかと、遭遇する可能性のあるさまざまな状況に合わせてこれらの機能をカスタマイズおよび最適化する方法について説明します。 また、requests
を効率的に使用する方法と、外部サービスへのリクエストによってアプリケーションの速度が低下するのを防ぐ方法についても学習します。
このチュートリアルでは、次の方法を学習します。
-
最も一般的なHTTPメソッドを使用するMake requests
-
クエリ文字列とメッセージ本文を使用して、リクエストのヘッダーとデータをCustomize
-
リクエストとレスポンスからのInspectデータ
-
authenticatedリクエストを行う
-
Configureは、アプリケーションのバックアップや速度低下を防ぐためのリクエストです。
この記事に含まれている機能と例を理解するために必要なだけの情報を含めようとしましたが、verybasic general knowledge of HTTPを想定しています。 とはいえ、とにかくうまくいくことができるかもしれません。
これで問題が解決したので、詳しく見て、アプリケーションでrequests
を使用する方法を見てみましょう。
requests
入門
requests
ライブラリをインストールすることから始めましょう。 これを行うには、次のコマンドを実行します。
$ pip install requests
Pythonパッケージの管理にPipenvを使用する場合は、次のコマンドを実行できます。
$ pipenv install requests
requests
をインストールすると、アプリケーションで使用できるようになります。 requests
のインポートは次のようになります。
import requests
これですべての設定が完了したので、requests
の旅を始めましょう。 最初の目標は、GET
リクエストを作成する方法を学ぶことです。
GETリクエスト
GET
やPOST
などのHTTP methodsは、HTTPリクエストを行うときに実行しようとしているアクションを決定します。 GET
とPOST
の他に、このチュートリアルの後半で使用する一般的な方法がいくつかあります。
最も一般的なHTTPメソッドの1つはGET
です。 GET
メソッドは、指定されたリソースからデータを取得または取得しようとしていることを示します。 GET
要求を行うには、requests.get()
を呼び出します。
これをテストするには、次のURLでget()
を呼び出すことにより、GitHubのRoot REST APIにGET
リクエストを送信できます。
>>>
>>> requests.get('https://api.github.com')
おめでとうございます。 最初のリクエストを行いました。 その要求の応答についてもう少し詳しく見てみましょう。
応答
Response
は、リクエストの結果を検査するための強力なオブジェクトです。 同じリクエストをもう一度行いますが、今回は戻り値を変数に保存して、その属性と動作を詳しく見てみましょう。
>>>
>>> response = requests.get('https://api.github.com')
この例では、Response
のインスタンスであるget()
の戻り値をキャプチャし、それをresponse
という変数に格納しました。 これで、response
を使用して、GET
リクエストの結果に関する多くの情報を確認できます。
状態コード
Response
から収集できる情報の最初のビットは、ステータスコードです。 ステータスコードにより、リクエストのステータスが通知されます。
たとえば、200 OK
ステータスはリクエストが成功したことを意味し、404 NOT FOUND
ステータスは探しているリソースが見つからなかったことを意味します。 リクエストで何が起こったかについての具体的な洞察を提供するmany other possible status codesもあります。
.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.')
Technical Detail:このTruth Value Testは、Response
上の__bool__()
is an overloaded methodのために可能になります。
これは、オブジェクトの真理値を決定するときにステータスコードを考慮に入れるために、Response
のデフォルトの動作が再定義されたことを意味します。
このメソッドはnotであり、ステータスコードが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(f'HTTP error occurred: {http_err}') # Python 3.6
except Exception as err:
print(f'Other error occurred: {err}') # Python 3.6
else:
print('Success!')
.raise_for_status()
を呼び出すと、特定のステータスコードに対してHTTPError
が発生します。 ステータスコードがリクエストの成功を示している場合、プログラムはその例外を発生させることなく続行します。
Further Reading: Python 3.6のf-stringsに慣れていない場合は、フォーマットされた文字列を単純化するための優れた方法であるため、これらを利用することをお勧めします。
これで、サーバーから返された応答のステータスコードを処理する方法について多くのことがわかりました。 ただし、GET
要求を行う場合、応答のステータスコードのみを気にすることはめったにありません。 通常、あなたはもっと見たいです。 次に、サーバーが応答の本文で送信した実際のデータを表示する方法を確認します。
コンテンツ
GET
要求の応答には、多くの場合、メッセージ本文にペイロードと呼ばれるいくつかの貴重な情報が含まれています。 Response
の属性とメソッドを使用して、さまざまな異なる形式でペイロードを表示できます。
応答の内容をbytes
で表示するには、.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などの文字エンコードを使用してそれらをstringに変換したい場合がよくあります。 .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}","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}"}'
bytes
からstr
へのデコードにはエンコード方式が必要なため、指定しない場合、requests
は応答のheadersに基づいてencodingを推測しようとします。 1。 .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}","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}"}'
応答を見ると、実際にシリアル化された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}', '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}'}
.json()
の戻り値のtype
は辞書であるため、キーを使用してオブジェクトの値にアクセスできます。
ステータスコードとメッセージ本文で多くのことができます。 ただし、応答自体に関するメタデータなどの詳細情報が必要な場合は、応答のヘッダーを確認する必要があります。
ヘッダ
応答ヘッダーは、応答ペイロードのコンテンツタイプや、応答をキャッシュする時間の制限など、有用な情報を提供できます。 これらのヘッダーを表示するには、.headers
にアクセスします。
>>>
>>> response.headers
{'Server': 'GitHub.com', 'Date': 'Mon, 10 Dec 2018 17:49:54 GMT', 'Content-Type': 'application/json; charset=utf-8', 'Transfer-Encoding': 'chunked', 'Status': '200 OK', 'X-RateLimit-Limit': '60', 'X-RateLimit-Remaining': '59', 'X-RateLimit-Reset': '1544467794', 'Cache-Control': 'public, max-age=60, s-maxage=60', 'Vary': 'Accept', 'ETag': 'W/"7dc470913f1fe9bb6c7355b50a0737bc"', 'X-GitHub-Media-Type': 'github.v3; format=json', 'Access-Control-Expose-Headers': 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type', 'Access-Control-Allow-Origin': '*', 'Strict-Transport-Security': 'max-age=31536000; includeSubdomains; preload', 'X-Frame-Options': 'deny', 'X-Content-Type-Options': 'nosniff', 'X-XSS-Protection': '1; mode=block', 'Referrer-Policy': 'origin-when-cross-origin, strict-origin-when-cross-origin', 'Content-Security-Policy': "default-src 'none'", 'Content-Encoding': 'gzip', 'X-GitHub-Request-Id': 'E439:4581:CF2351:1CA3E06:5C0EA741'}
.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
リクエストをカスタマイズする一般的な方法の1つは、URLのquery stringパラメーターを介して値を渡すことです。 get()
を使用してこれを行うには、データをparams
に渡します。 たとえば、GitHubのSearch APIを使用して、requests
ライブラリを検索できます。
import requests
# Search GitHub's 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(f'Repository name: {repository["name"]}') # Python 3.6+
print(f'Repository description: {repository["description"]}') # Python 3.6+
辞書{'q': 'requests+language:python'}
を.get()
のparams
パラメーターに渡すことにより、SearchAPIから返される結果を変更できます。
params
をget()
に、今行ったように辞書の形式で、またはタプルのリストとして渡すことができます。
>>>
>>> requests.get(
... 'https://api.github.com/search/repositories',
... params=[('q', 'requests+language:python')],
... )
値をbytes
として渡すこともできます。
>>>
>>> requests.get(
... 'https://api.github.com/search/repositories',
... params=b'q=requests+language:python',
... )
クエリ文字列は、GET
リクエストのパラメータ化に役立ちます。 送信するヘッダーを追加または変更して、リクエストをカスタマイズすることもできます。
リクエストヘッダ
ヘッダーをカスタマイズするには、headers
パラメータを使用してHTTPヘッダーのディクショナリをget()
に渡します。 たとえば、Accept
ヘッダーでtext-match
メディアタイプを指定することにより、以前の検索リクエストを変更して、結果で一致する検索用語を強調表示できます。
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(f'Text matches: {repository["text_matches"]}')
Accept
ヘッダーは、アプリケーションが処理できるコンテンツタイプをサーバーに通知します。 この場合、一致する検索用語が強調表示されることを期待しているため、ヘッダー値application/vnd.github.v3.text-match+json
を使用しています。これは、コンテンツが特別なJSON形式である独自のGitHubAccept
ヘッダーです。
リクエストをカスタマイズする他の方法を学ぶ前に、他の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']
{}
ヘッダー、応答本文、ステータスコードなどは、各メソッドのResponse
で返されます。 次に、POST
、PUT
、およびPATCH
メソッドを詳しく見て、他のリクエストタイプとの違いを学びます。
メッセージ本文
HTTP仕様によれば、POST
、PUT
、およびあまり一般的ではないPATCH
要求は、クエリ文字列のパラメーターではなく、メッセージ本文を介してデータを渡します。 requests
を使用して、ペイロードを対応する関数のdata
パラメータに渡します。
data
は、辞書、タプル、バイトのリスト、またはファイルのようなオブジェクトを取ります。 リクエストの本文で送信するデータを、やり取りするサービスの特定のニーズに合わせて調整する必要があります。
たとえば、リクエストのコンテンツタイプがapplication/x-www-form-urlencoded
の場合、フォームデータを辞書として送信できます。
>>>
>>> requests.post('https://httpbin.org/post', data={'key':'value'})
同じデータをタプルのリストとして送信することもできます。
>>>
>>> requests.post('https://httpbin.org/post', data=[('key', 'value')])
ただし、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、ヘッダー、認証など、行われている要求に関するあらゆる種類の情報にアクセスできます。
これまで、さまざまな種類のリクエストを数多く行ってきましたが、それらには共通点が1つあります。それは、パブリックAPIに対する認証されていないリクエストです。 あなたが出くわすかもしれない多くのサービスは、何らかの方法であなたに認証をして欲しいでしょう。
認証
認証は、サービスがあなたが誰であるかを理解するのに役立ちます。 通常、Authorization
ヘッダーまたはサービスによって定義されたカスタムヘッダーを介してデータを渡すことにより、サーバーに資格情報を提供します。 これまで見てきたすべてのリクエスト関数は、auth
というパラメータを提供します。これにより、認証情報を渡すことができます。
認証が必要なAPIの一例はGitHubのAuthenticated UserAPIです。 このエンドポイントは、認証されたユーザーのプロファイルに関する情報を提供します。 Authenticated User APIにリクエストを送信するには、GitHubのユーザー名とパスワードをタプルでget()
に渡すことができます。
>>>
>>> from getpass import getpass
>>> requests.get('https://api.github.com/user', auth=('username', getpass()))
タプルでauth
に渡した資格情報が有効な場合、要求は成功しました。 認証情報なしでこのリクエストを行おうとすると、ステータスコードが401 Unauthorized
であることがわかります。
>>>
>>> requests.get('https://api.github.com/user')
ユーザー名とパスワードをタプルでauth
パラメータに渡すと、requests
は内部でHTTPのBasic access authentication schemeを使用して資格情報を適用します。
したがって、HTTPBasicAuth
を使用して明示的な基本認証資格情報を渡すことにより、同じ要求を行うことができます。
>>>
>>> from requests.auth import HTTPBasicAuth
>>> from getpass import getpass
>>> requests.get(
... 'https://api.github.com/user',
... auth=HTTPBasicAuth('username', getpass())
... )
基本認証を明示的に指定する必要はありませんが、別の方法を使用して認証することもできます。 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証明書の検証を無効にする場合は、リクエスト関数のverify
パラメータにFalse
を渡します。
>>>
>>> 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)
requests
は、データを安全に保つために安全でないリクエストを行っている場合でも警告を発します。
Note:requests
uses a package called certifi
は、認証局を提供します。 これにより、requests
は信頼できる権限を知ることができます。 したがって、接続を可能な限り安全に保つために、certifi
を頻繁に更新する必要があります。
パフォーマンス
requests
を使用する場合、特に実稼働アプリケーション環境では、パフォーマンスへの影響を考慮することが重要です。 タイムアウト制御、セッション、再試行制限などの機能は、アプリケーションをスムーズに実行し続けるのに役立ちます。
タイムアウト
外部サービスにインラインリクエストを行う場合、システムは先に進む前に応答を待つ必要があります。 アプリケーションがその応答を待ちすぎると、サービスへのリクエストがバックアップされたり、ユーザーエクスペリエンスが低下したり、バックグラウンドジョブがハングしたりする可能性があります。
デフォルトでは、requests
は応答を無期限に待機するため、ほとんどの場合、これらの事態が発生しないようにタイムアウト期間を指定する必要があります。 リクエストのタイムアウトを設定するには、timeout
パラメータを使用します。 timeout
は、タイムアウトする前に応答を待機する秒数を表す整数または浮動小数点数にすることができます。
>>>
>>> requests.get('https://api.github.com', timeout=1)
>>> requests.get('https://api.github.com', timeout=3.05)
最初のリクエストでは、リクエストは1秒後にタイムアウトします。 2番目の要求では、要求は3.05秒後にタイムアウトします。
You can also pass a tupleからtimeout
で、最初の要素は接続タイムアウト(クライアントがサーバーへの接続を確立できる時間)であり、2番目の要素は読み取りタイムアウト(待機する時間)です。クライアントが接続を確立した後の応答):
>>>
>>> requests.get('https://api.github.com', timeout=(2, 5))
要求が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
例外をキャッチし、それに応じて応答できます。
セッションオブジェクト
これまで、get()
やpost()
などの高レベルのrequests
APIを扱ってきました。 これらの機能は、リクエストを行ったときに何が起こっているかを抽象化したものです。 接続をどのように管理するかなど、実装の詳細を隠すため、接続について心配する必要はありません。
これらの抽象化の下には、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(), you'll 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
を使用してサーバーに接続すると、その接続は接続プールに保持されます。 アプリが同じサーバーに再度接続する場合、新しい接続を確立するのではなく、プールからの接続を再利用します。
最大再試行
要求が失敗した場合、アプリケーションに同じ要求を再試行させることができます。 ただし、requests
はデフォルトではこれを行いません。 この機能を適用するには、カスタムTransport Adapterを実装する必要があります。
トランスポートアダプターを使用すると、やり取りするサービスごとに構成のセットを定義できます。 たとえば、https://api.github.com
へのすべてのリクエストを3回再試行してから、最終的にConnectionError
を発生させたいとします。 トランスポートアダプタを作成し、その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への各要求の構成に従います。
タイムアウト、トランスポートアダプター、およびセッションは、コードの効率とアプリケーションの復元力を維持するためのものです。
結論
Pythonの強力なrequests
ライブラリについて学ぶのに長い道のりを歩んできました。
次のことができるようになりました。
-
GET
、POST
、PUT
などのさまざまなHTTPメソッドを使用してリクエストを作成します -
ヘッダー、認証、クエリ文字列、メッセージ本文を変更してリクエストをカスタマイズします
-
サーバーに送信するデータと、サーバーから返送されるデータを検査します
-
SSL証明書の検証を使用する
-
max_retries
、timeout
、セッション、およびトランスポートアダプタを使用してrequests
を効果的に使用する
requests
の使用方法を学んだので、Webサービスの幅広い世界を探索し、それらが提供する魅力的なデータを使用してすばらしいアプリケーションを構築する準備が整いました。