Pythonのリクエストライブラリ(ガイド)

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リクエスト

GETPOSTなどのHTTP methodsは、HTTPリクエストを行うときに実行しようとしているアクションを決定します。 GETPOSTの他に、このチュートリアルの後半で使用する一般的な方法がいくつかあります。

最も一般的なHTTPメソッドの1つはGETです。 GETメソッドは、指定されたリソースからデータを取得または取得しようとしていることを示します。 GET要求を行うには、requests.get()を呼び出します。

これをテストするには、次のURLでget()を呼び出すことにより、GitHubのRoot REST APIGETリクエストを送信できます。

>>>

>>> 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_code200を返しました。これは、要求が成功し、サーバーが要求したデータで応答したことを意味します。

場合によっては、この情報を使用してコードの決定を行うことができます。

if response.status_code == 200:
    print('Success!')
elif response.status_code == 404:
    print('Not Found.')

このロジックでは、サーバーが200ステータスコードを返すと、プログラムはSuccess!を出力します。 結果が404の場合、プログラムはNot Foundを出力します。

requestsは、このプロセスを簡素化するためにさらに一歩進んでいます。 条件式でResponseインスタンスを使用する場合、ステータスコードが200400の間にある場合は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 CONTENT304 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から返される結果を変更できます。

paramsget()に、今行ったように辞書の形式で、またはタプルのリストとして渡すことができます。

>>>

>>> 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メソッドには、POSTPUTDELETEHEADPATCH、および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で返されます。 次に、POSTPUT、およびPATCHメソッドを詳しく見て、他のリクエストタイプとの違いを学びます。

メッセージ本文

HTTP仕様によれば、POSTPUT、およびあまり一般的ではない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は、requestsKenneth 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は、HTTPDigestAuthHTTPProxyAuthなど、すぐに使用できる他の認証方法を提供します。

独自の認証メカニズムを提供することもできます。 これを行うには、最初に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()などの高レベルのrequestsAPIを扱ってきました。 これらの機能は、リクエストを行ったときに何が起こっているかを抽象化したものです。 接続をどのように管理するかなど、実装の詳細を隠すため、接続について心配する必要はありません。

これらの抽象化の下には、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)

HTTPAdaptergithub_adaptersessionにマウントすると、sessionhttps://api.github.comへの各要求の構成に従います。

タイムアウト、トランスポートアダプター、およびセッションは、コードの効率とアプリケーションの復元力を維持するためのものです。

結論

Pythonの強力なrequestsライブラリについて学ぶのに長い道のりを歩んできました。

次のことができるようになりました。

  • GETPOSTPUTなどのさまざまなHTTPメソッドを使用してリクエストを作成します

  • ヘッダー、認証、クエリ文字列、メッセージ本文を変更してリクエストをカスタマイズします

  • サーバーに送信するデータと、サーバーから返送されるデータを検査します

  • SSL証明書の検証を使用する

  • max_retriestimeout、セッション、およびトランスポートアダプタを使用してrequestsを効果的に使用する

requestsの使用方法を学んだので、Webサービスの幅広い世界を探索し、それらが提供する魅力的なデータを使用してすばらしいアプリケーションを構築する準備が整いました。