高度な使用法
このドキュメントでは、リクエストのより高度な機能の一部について説明します。
セッションオブジェクト
Sessionオブジェクトを使用すると、リクエスト間で特定のパラメータを永続化できます。 また、Sessionインスタンスから行われたすべてのリクエストにわたってCookieを保持し、urllib3
の接続プールを使用します。 そのため、同じホストに対して複数のリクエストを行う場合、基盤となるTCP接続が再利用され、パフォーマンスが大幅に向上する可能性があります( HTTP持続的接続を参照)。
Sessionオブジェクトには、メインのRequestsAPIのすべてのメソッドがあります。
リクエスト間でいくつかのCookieを保持しましょう:
s = requests.Session()
s.get('https://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get('https://httpbin.org/cookies')
print(r.text)
# '{"cookies": {"sessioncookie": "123456789"}}'
セッションを使用して、リクエストメソッドにデフォルトデータを提供することもできます。 これは、Sessionオブジェクトのプロパティにデータを提供することによって行われます。
s = requests.Session()
s.auth = ('user', 'pass')
s.headers.update({'x-test': 'true'})
# both 'x-test' and 'x-test2' are sent
s.get('https://httpbin.org/headers', headers={'x-test2': 'true'})
リクエストメソッドに渡すディクショナリは、設定されているセッションレベルの値とマージされます。 メソッドレベルのパラメータは、セッションパラメータを上書きします。
ただし、セッションを使用している場合でも、メソッドレベルのパラメータはリクエスト間で保持されないことに注意してください。 この例では、最初のリクエストでのみCookieを送信し、2番目のリクエストでは送信しません。
s = requests.Session()
r = s.get('https://httpbin.org/cookies', cookies={'from-my': 'browser'})
print(r.text)
# '{"cookies": {"from-my": "browser"}}'
r = s.get('https://httpbin.org/cookies')
print(r.text)
# '{"cookies": {}}'
セッションに手動でCookieを追加する場合は、 Cookieユーティリティ関数を使用して Session.cookies を操作します。
セッションは、コンテキストマネージャーとしても使用できます。
with requests.Session() as s:
s.get('https://httpbin.org/cookies/set/sessioncookie/123456789')
これにより、未処理の例外が発生した場合でも、with
ブロックが終了するとすぐにセッションが確実に閉じられます。
ディクトパラメータから値を削除する
dictパラメーターからセッションレベルのキーを省略したい場合があります。 これを行うには、メソッドレベルのパラメーターでそのキーの値をNone
に設定するだけです。 自動的に省略されます。
セッションに含まれるすべての値は、直接利用できます。 詳細については、 Session API Docs を参照してください。
要求オブジェクトと応答オブジェクト
requests.get()
やその友人に電話をかけるときはいつでも、2つの主要なことをしていることになります。 まず、Request
オブジェクトを作成します。このオブジェクトは、サーバーに送信されて、リソースを要求または照会します。 次に、Requestsがサーバーから応答を受け取ると、Response
オブジェクトが生成されます。 Response
オブジェクトには、サーバーから返されるすべての情報が含まれ、最初に作成したRequest
オブジェクトも含まれます。 ウィキペディアのサーバーからいくつかの非常に重要な情報を取得するための簡単なリクエストは次のとおりです。
>>> r = requests.get('https://en.wikipedia.org/wiki/Monty_Python')
サーバーから返送されたヘッダーにアクセスする場合は、次のようにします。
>>> r.headers
{'content-length': '56170', 'x-content-type-options': 'nosniff', 'x-cache':
'HIT from cp1006.eqiad.wmnet, MISS from cp1010.eqiad.wmnet', 'content-encoding':
'gzip', 'age': '3080', 'content-language': 'en', 'vary': 'Accept-Encoding,Cookie',
'server': 'Apache', 'last-modified': 'Wed, 13 Jun 2012 01:33:50 GMT',
'connection': 'close', 'cache-control': 'private, s-maxage=0, max-age=0,
must-revalidate', 'date': 'Thu, 14 Jun 2012 12:59:39 GMT', 'content-type':
'text/html; charset=UTF-8', 'x-cache-lookup': 'HIT from cp1006.eqiad.wmnet:3128,
MISS from cp1010.eqiad.wmnet:80'}
ただし、サーバーに送信したヘッダーを取得する場合は、リクエストにアクセスしてから、リクエストのヘッダーにアクセスします。
>>> r.request.headers
{'Accept-Encoding': 'identity, deflate, compress, gzip',
'Accept': '*/*', 'User-Agent': 'python-requests/1.2.0'}
準備されたリクエスト
API呼び出しまたはセッション呼び出しから Response オブジェクトを受信する場合は常に、request
属性は実際に使用されたPreparedRequest
です。 場合によっては、リクエストを送信する前に、本文またはヘッダー(または実際には他の何か)に追加の作業を行うことをお勧めします。 このための簡単なレシピは次のとおりです。
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)
Request
オブジェクトでは特別なことは何もしていないので、すぐに準備してPreparedRequest
オブジェクトを変更します。 次に、requests.*
またはSession.*
に送信する他のパラメーターと一緒に送信します。
ただし、上記のコードでは、Requests Session オブジェクトを持つことの利点の一部が失われます。 特に、Cookieなどの Session レベルの状態はリクエストに適用されません。 その状態が適用された PreparedRequest を取得するには、次のように、 Request.prepare()の呼び出しを 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)
準備されたリクエストフローを使用する場合、環境を考慮していないことに注意してください。 これは、環境変数を使用してリクエストの動作を変更する場合に問題を引き起こす可能性があります。 例:REQUESTS_CA_BUNDLE
で指定された自己署名SSL証明書は考慮されません。 その結果、SSL: CERTIFICATE_VERIFY_FAILED
がスローされます。 この動作を回避するには、環境設定をセッションに明示的にマージします。
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)
SSL証明書の検証
Requestsは、Webブラウザーと同様に、HTTPS要求のSSL証明書を検証します。 デフォルトでは、SSL検証が有効になっており、証明書を検証できない場合、リクエストはSSLErrorをスローします。
>>> requests.get('https://requestb.in')
requests.exceptions.SSLError: hostname 'requestb.in' doesn't match either of '*.herokuapp.com', 'herokuapp.com'
このドメインにSSLが設定されていないため、例外がスローされます。 優秀な。 ただし、GitHubは次のことを行います。
>>> requests.get('https://github.com')
<Response [200]>
verify
に、信頼できるCAの証明書を含むCA_BUNDLEファイルまたはディレクトリへのパスを渡すことができます。
>>> requests.get('https://github.com', verify='/path/to/certfile')
または永続的:
s = requests.Session()
s.verify = '/path/to/certfile'
ノート
verify
がディレクトリへのパスに設定されている場合、ディレクトリはOpenSSLに付属のc_rehash
ユーティリティを使用して処理されている必要があります。
この信頼できるCAのリストは、REQUESTS_CA_BUNDLE
環境変数を使用して指定することもできます。 REQUESTS_CA_BUNDLE
が設定されていない場合、CURL_CA_BUNDLE
がフォールバックとして使用されます。
verify
をFalseに設定した場合、リクエストはSSL証明書の検証を無視することもできます。
>>> requests.get('https://kennethreitz.org', verify=False)
<Response [200]>
verify
がFalse
に設定されている場合、リクエストはサーバーによって提示されたTLS証明書を受け入れ、ホスト名の不一致や期限切れの証明書を無視するため、アプリケーションが中間者に対して脆弱になることに注意してください。インザミドル(MitM)攻撃。 検証をFalse
に設定すると、ローカル開発またはテスト中に役立つ場合があります。
デフォルトでは、verify
はTrueに設定されています。 オプションverify
は、ホスト証明書にのみ適用されます。
クライアント側の証明書
クライアント側の証明書として、単一のファイル(秘密鍵と証明書を含む)として、または両方のファイルのパスのタプルとして使用するローカル証明書を指定することもできます。
>>> requests.get('https://kennethreitz.org', cert=('/path/client.cert', '/path/client.key'))
<Response [200]>
または永続的:
s = requests.Session()
s.cert = '/path/client.cert'
間違ったパスまたは無効な証明書を指定すると、SSLErrorが発生します。
>>> requests.get('https://kennethreitz.org', cert='/wrong_path/client.pem')
SSLError: [Errno 336265225] _ssl.c:347: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib
警告
ローカル証明書の秘密鍵は暗号化されていない必要があります。 現在、リクエストは暗号化されたキーの使用をサポートしていません。
CA証明書
Requestsは、パッケージ certifi の証明書を使用します。 これにより、ユーザーはリクエストのバージョンを変更せずに信頼できる証明書を更新できます。
バージョン2.16より前では、Requestsは、 Mozillaトラストストアから供給された信頼できるルートCAのセットをバンドルしていました。 証明書は、Requestsバージョンごとに1回だけ更新されました。 certifi
がインストールされていない場合、かなり古いバージョンのリクエストを使用すると、証明書バンドルが非常に古くなりました。
セキュリティ上の理由から、certifiを頻繁にアップグレードすることをお勧めします。
ボディコンテンツワークフロー
デフォルトでは、リクエストを行うと、レスポンスの本文がすぐにダウンロードされます。 stream
パラメーターを使用して Response.content 属性にアクセスするまで、この動作をオーバーライドして、応答本文のダウンロードを延期することができます。
tarball_url = 'https://github.com/psf/requests/tarball/master'
r = requests.get(tarball_url, stream=True)
この時点では、応答ヘッダーのみがダウンロードされ、接続は開いたままなので、コンテンツの取得を条件付きにすることができます。
if int(r.headers['content-length']) < TOO_LONG:
content = r.content
...
Response.iter_content()および Response.iter_lines()メソッドを使用して、ワークフローをさらに制御できます。 または、 Response.raw の基になるurllib3 urllib3.HTTPResponse
からデコードされていない本文を読み取ることもできます。
リクエスト時にstream
をTrue
に設定すると、すべてのデータを消費するか、 Response.close を呼び出さない限り、リクエストは接続をプールに解放できません。 これにより、接続が非効率になる可能性があります。 stream=True
の使用中にリクエスト本文を部分的に読んでいる(またはまったく読んでいない)場合は、with
ステートメント内でリクエストを実行して、常に閉じていることを確認する必要があります。
with requests.get('https://httpbin.org/get', stream=True) as r:
# Do things with the response here.
生き続ける
すばらしいニュース— urllib3のおかげで、セッション内でのキープアライブは100%自動化されています。 セッション内で行うリクエストはすべて、適切な接続を自動的に再利用します。
接続は、すべての本文データが読み取られた後でのみ、再利用のためにプールに解放されることに注意してください。 必ずstream
をFalse
に設定するか、Response
オブジェクトのcontent
プロパティを読み取ってください。
ストリーミングアップロード
Requestsはストリーミングアップロードをサポートしています。これにより、大きなストリームやファイルをメモリに読み込まずに送信できます。 ストリーミングとアップロードを行うには、ファイルのようなオブジェクトを体に提供するだけです。
with open('massive-body', 'rb') as f:
requests.post('http://some.url/streamed', data=f)
警告
バイナリモードでファイルを開くことを強くお勧めします。 これは、リクエストがContent-Length
ヘッダーを提供しようとする可能性があるためです。提供される場合、この値はファイル内のバイトの数に設定されます。 テキストモードでファイルを開くと、エラーが発生する場合があります。
チャンクエンコードされたリクエスト
Requestsは、送信要求と受信要求のチャンク転送エンコーディングもサポートします。 チャンクエンコードされたリクエストを送信するには、ボディにジェネレーター(または長さのないイテレーター)を提供するだけです。
def gen():
yield 'hi'
yield 'there'
requests.post('http://some.url/chunked', data=gen())
チャンク化されたエンコードされた応答の場合、 Response.iter_content()を使用してデータを反復処理するのが最善です。 理想的な状況では、リクエストにstream=True
を設定します。その場合、None
。 チャンクの最大サイズを設定する場合は、chunk_size
パラメーターを任意の整数に設定できます。
複数のマルチパートエンコードファイルをPOSTする
1つのリクエストで複数のファイルを送信できます。 たとえば、複数のファイルフィールド 'images'を持つHTMLフォームに画像ファイルをアップロードするとします。
<input type="file" name="images" multiple="true" required="true"/>
これを行うには、ファイルを(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
{
...
'files': {'images': 'data:image/png;base64,iVBORw ....'}
'Content-Type': 'multipart/form-data; boundary=3131623adb2043caaeb5538cc7aa0b3a',
...
}
警告
バイナリモードでファイルを開くことを強くお勧めします。 これは、リクエストがContent-Length
ヘッダーを提供しようとする可能性があるためです。提供される場合、この値はファイル内のバイトの数に設定されます。 テキストモードでファイルを開くと、エラーが発生する場合があります。
イベントフック
Requestsには、リクエストプロセスの一部を操作したり、イベント処理を通知したりするために使用できるフックシステムがあります。
利用可能なフック:
response
:- リクエストから生成されたレスポンス。
{hook_name: callback_function}
ディクショナリをhooks
リクエストパラメータに渡すことで、リクエストごとにフック関数を割り当てることができます。
hooks={'response': print_url}
そのcallback_function
は、最初の引数としてデータのチャンクを受け取ります。
def print_url(r, *args, **kwargs):
print(r.url)
コールバック関数は、独自の例外を処理する必要があります。 未処理の例外はサイレントに渡されないため、Requestsを呼び出すコードで処理する必要があります。
コールバック関数が値を返す場合、渡されたデータを置き換えるものと見なされます。 関数が何も返さない場合、他には何も影響しません。
def record_hook(r, *args, **kwargs):
r.hook_called = True
return r
実行時にいくつかのリクエストメソッド引数を出力してみましょう。
>>> requests.get('https://httpbin.org/', hooks={'response': print_url})
https://httpbin.org/
<Response [200]>
1つのリクエストに複数のフックを追加できます。 一度に2つのフックを呼び出しましょう:
>>> r = requests.get('https://httpbin.org/', hooks={'response': [print_url, record_hook]})
>>> r.hook_called
True
Session
インスタンスにフックを追加することもできます。 追加したフックは、セッションに対して行われるすべてのリクエストで呼び出されます。 例えば:
>>> s = requests.Session()
>>> s.hooks['response'].append(print_url)
>>> s.get('https://httpbin.org/')
https://httpbin.org/
<Response [200]>
Session
は複数のフックを持つことができ、それらは追加された順序で呼び出されます。
カスタム認証
リクエストを使用すると、独自の認証メカニズムを指定できます。
auth
引数としてリクエストメソッドに渡されるcallableは、ディスパッチされる前にリクエストを変更する機会があります。
認証の実装は AuthBase のサブクラスであり、簡単に定義できます。 Requestsは、requests.auth
で、 HTTPBasicAuth と HTTPDigestAuth の2つの一般的な認証スキームの実装を提供します。
X-Pizza
ヘッダーがパスワード値に設定されている場合にのみ応答するWebサービスがあるとしましょう。 可能性は低いですが、それと一緒に行ってください。
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
次に、PizzaAuthを使用してリクエストを行うことができます。
>>> requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth'))
<Response [200]>
ストリーミングリクエスト
Response.iter_lines()を使用すると、 TwitterストリーミングAPI などのストリーミングAPIを簡単に反復できます。 stream
をTrue
に設定し、 iter_lines で応答を繰り返すだけです。
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 = True を Response.iter_lines()または Response.iter_content()とともに使用する場合、サーバーがイベントに備えてフォールバックエンコーディングを提供する必要があります。提供していません:
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 は再入可能ではありません。 このメソッドを複数回呼び出すと、受信したデータの一部が失われます。 複数の場所から呼び出す必要がある場合は、代わりに結果のイテレータオブジェクトを使用してください。
lines = r.iter_lines()
# Save the first line for later or just skip it
first_line = next(lines)
for line in lines:
print(line)
プロキシ
プロキシを使用する必要がある場合は、任意のリクエストメソッドのproxies
引数を使用して個々のリクエストを構成できます。
import requests
proxies = {
'http': 'http://10.10.1.10:3128',
'https': 'http://10.10.1.10:1080',
}
requests.get('http://example.org', proxies=proxies)
または、セッション全体に対して1回構成することもできます。
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')
上記のようにPythonでプロキシ設定が上書きされない場合、デフォルトでは、リクエストは標準環境変数http_proxy
、https_proxy
、no_proxy
、curl_ca_bundle
。 これらの変数の大文字のバリアントもサポートされています。 したがって、リクエストを構成するように設定できます(ニーズに関連するもののみを設定します)。
$ export HTTP_PROXY="http://10.10.1.10:3128"
$ export HTTPS_PROXY="http://10.10.1.10:1080"
$ python
>>> import requests
>>> requests.get('http://example.org')
プロキシでHTTP基本認証を使用するには、上記の構成エントリのいずれかで http:// user:password @ host / 構文を使用します。
$ export HTTPS_PROXY="http://user:[email protected]:1080"
$ python
>>> proxies = {'http': 'http://user:[email protected]:3128/'}
警告
機密性の高いユーザー名とパスワードの情報を環境変数またはバージョン管理されたファイルに保存することはセキュリティリスクであり、強くお勧めしません。
特定のスキームとホストのプロキシを提供するには、キーに schemae:// hostname フォームを使用します。 これは、指定されたスキームと正確なホスト名へのすべての要求に一致します。
proxies = {'http://10.20.1.128': 'http://10.10.1.10:5323'}
プロキシURLにはスキームが含まれている必要があることに注意してください。
最後に、https接続にプロキシを使用するには、通常、ローカルマシンがプロキシのルート証明書を信頼する必要があることに注意してください。 デフォルトでは、リクエストによって信頼される証明書のリストは次の場所にあります。
from requests.utils import DEFAULT_CA_BUNDLE_PATH
print(DEFAULT_CA_BUNDLE_PATH)
このデフォルトの証明書バンドルを上書きするには、標準のcurl_ca_bundle
環境変数を別のファイルパスに設定します。
$ export curl_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')
靴下
バージョン2.10.0の新機能。
基本的なHTTPプロキシに加えて、RequestsはSOCKSプロトコルを使用するプロキシもサポートします。 これはオプションの機能であり、使用する前に追加のサードパーティライブラリをインストールする必要があります。
この機能の依存関係はpip
から取得できます。
$ python -m pip install requests[socks]
これらの依存関係をインストールすると、SOCKSプロキシの使用はHTTPプロキシの使用と同じくらい簡単です。
proxies = {
'http': 'socks5://user:pass@host:port',
'https': 'socks5://user:pass@host:port'
}
スキームsocks5
を使用すると、DNS解決がプロキシサーバーではなくクライアントで発生します。 これは、このスキームを使用してクライアントとプロキシのどちらでDNS解決を行うかを決定するcurlと一致しています。 プロキシサーバー上のドメインを解決する場合は、スキームとしてsocks5h
を使用します。
コンプライアンス
リクエストは、関連するすべての仕様とRFCに準拠することを目的としており、その準拠によってユーザーに問題が発生することはありません。 仕様へのこの注意は、関連する仕様に精通していない人には異常に見えるかもしれないいくつかの振る舞いにつながる可能性があります。
エンコーディング
応答を受信すると、Requestsは、 Response.text 属性にアクセスしたときに応答のデコードに使用するエンコードを推測します。 リクエストは最初にHTTPヘッダーのエンコードをチェックし、エンコードが存在しない場合は、 charset_normalizer または chardet を使用してエンコードを推測しようとします。
chardet
がインストールされている場合、requests
はそれを使用しますが、python3の場合、chardet
は必須の依存関係ではなくなりました。 chardet
ライブラリはLGPLライセンスの依存関係であり、リクエストの一部のユーザーは必須のLGPLライセンスの依存関係に依存できません。
[use_chardet_on_py3]]
を追加指定せずにrequest
をインストールし、chardet
がまだインストールされていない場合、requests
はcharset-normalizer
(MITライセンス)を使用してエンコーディングを推測します。 Python 2の場合、requests
はchardet
のみを使用し、そこでは必須の依存関係です。
Requestsがエンコーディングを推測しないのは、HTTPヘッダーおよびに明示的な文字セットが存在しない場合のみです。Content-Type
ヘッダーにはtext
が含まれます。 この状況では、 RFC 2616 は、デフォルトの文字セットがISO-8859-1
でなければならないことを指定しています。 この場合、リクエストは仕様に従います。 別のエンコーディングが必要な場合は、 Response.encoding プロパティを手動で設定するか、生の Response.content を使用できます。
HTTP動詞
Requestsは、ほぼすべての範囲のHTTP動詞(GET、OPTIONS、HEAD、POST、PUT、PATCH、およびDELETE)へのアクセスを提供します。 以下に、GitHub APIを使用して、リクエストでこれらのさまざまな動詞を使用する詳細な例を示します。
最も一般的に使用される動詞GETから始めます。 HTTP GETは、指定されたURLからリソースを返すべき等メソッドです。 結果として、これはWebロケーションからデータを取得しようとするときに使用する必要がある動詞です。 使用例は、GitHubから特定のコミットに関する情報を取得しようとすることです。 リクエストでa050faf
をコミットしたいとします。 私たちはそれを次のように取得します:
>>> import requests
>>> r = requests.get('https://api.github.com/repos/psf/requests/git/commits/a050faf084662f3a352dd1a941f2c7c9f886d4ad')
GitHubが正しく応答したことを確認する必要があります。 ある場合は、それがどのような種類のコンテンツであるかを調べたいと思います。 このようにしてください:
>>> if r.status_code == requests.codes.ok:
... print(r.headers['content-type'])
...
application/json; charset=utf-8
したがって、GitHubはJSONを返します。 それは素晴らしいことです。 r.json メソッドを使用してPythonオブジェクトに解析できます。
>>> commit_data = r.json()
>>> print(commit_data.keys())
['committer', 'author', 'url', 'tree', 'sha', 'parents', 'message']
>>> print(commit_data['committer'])
{'date': '2012-05-10T11:10:50-07:00', 'email': '[email protected]', 'name': 'Kenneth Reitz'}
>>> print(commit_data['message'])
makin' history
これまでのところ、とても簡単です。 さて、GitHubAPIを少し調べてみましょう。 これでドキュメントを見ることができましたが、代わりにリクエストを使用すると、もう少し楽しいかもしれません。 Requests OPTIONS動詞を利用して、使用したURLでサポートされているHTTPメソッドの種類を確認できます。
>>> verbs = requests.options(r.url)
>>> verbs.status_code
500
ええと、何? それは役に立たない! 多くのAPIプロバイダーと同様に、GitHubは実際にはOPTIONSメソッドを実装していません。 これは厄介な見落としですが、大丈夫です。退屈なドキュメントを使用するだけです。 ただし、GitHubがOPTIONSを正しく実装している場合は、ヘッダーで許可されているメソッドを返す必要があります。
>>> verbs = requests.options('http://a-good-website.com/api/cats')
>>> print(verbs.headers['allow'])
GET,HEAD,POST,OPTIONS
ドキュメントを見ると、コミットに許可されている他のメソッドはPOSTだけであり、これは新しいコミットを作成します。 Requestsリポジトリを使用しているので、手に負えないPOSTを作成することはおそらく避けてください。 代わりに、GitHubのIssues機能を試してみましょう。
このドキュメントは、 Issue#482 に対応して追加されました。 この問題はすでに存在するため、例として使用します。 それを取得することから始めましょう。
>>> r = requests.get('https://api.github.com/repos/psf/requests/issues/482')
>>> r.status_code
200
>>> issue = json.loads(r.text)
>>> print(issue['title'])
Feature any http verb in docs
>>> print(issue['comments'])
3
かっこいい、3つのコメントがあります。 それらの最後を見てみましょう。
>>> r = requests.get(r.url + '/comments')
>>> r.status_code
200
>>> comments = r.json()
>>> print(comments[0].keys())
['body', 'url', 'created_at', 'updated_at', 'user', 'id']
>>> print(comments[2]['body'])
Probably in the "advanced" section
まあ、それはばかげた場所のようです。 彼がばかげていることをポスターに伝えるコメントを投稿しましょう。 とにかく、ポスターは誰ですか?
>>> print(comments[2]['user']['login'])
kennethreitz
では、このケネスの人に、この例は代わりにクイックスタートガイドに記載する必要があると考えていることを伝えましょう。 GitHub APIドキュメントによると、これを行う方法はスレッドにPOSTすることです。 やってみましょう。
>>> body = json.dumps({u"body": u"Sounds great! I'll get right on it!"})
>>> url = u"https://api.github.com/repos/psf/requests/issues/482/comments"
>>> r = requests.post(url=url, data=body)
>>> r.status_code
404
ええと、それは奇妙です。 おそらく認証が必要です。 それは苦痛でしょうね? 間違い。 リクエストを使用すると、非常に一般的な基本認証など、さまざまな形式の認証を簡単に使用できます。
>>> from requests.auth import HTTPBasicAuth
>>> auth = HTTPBasicAuth('[email protected]', 'not_a_real_password')
>>> r = requests.post(url=url, data=body, auth=auth)
>>> r.status_code
201
>>> content = r.json()
>>> print(content['body'])
Sounds great! I'll get right on it.
素晴らしい。 ああ、待って、いや! 猫に餌をやらなければならなかったので、しばらく時間がかかると付け加えました。 このコメントを編集できれば! 幸い、GitHubでは、別のHTTP動詞PATCHを使用してこのコメントを編集できます。 それをしましょう。
>>> print(content[u"id"])
5804413
>>> body = json.dumps({u"body": u"Sounds great! I'll get right on it once I feed my cat."})
>>> url = u"https://api.github.com/repos/psf/requests/issues/comments/5804413"
>>> r = requests.patch(url=url, data=body, auth=auth)
>>> r.status_code
200
優秀な。 さて、このケネスの男を拷問するために、私は彼に汗を流させ、私がこれに取り組んでいることを彼に言わないことに決めました。 つまり、このコメントを削除したいということです。 GitHubでは、非常に適切な名前のDELETEメソッドを使用してコメントを削除できます。 それを取り除きましょう。
>>> r = requests.delete(url=url, auth=auth)
>>> r.status_code
204
>>> r.headers['status']
'204 No Content'
優秀な。 全部なくなった。 私が知りたい最後のことは、私が使用したレート制限の量です。 確認してみましょう。 GitHubはその情報をヘッダーで送信するため、ページ全体をダウンロードするのではなく、ヘッダーを取得するためにHEADリクエストを送信します。
>>> r = requests.head(url=url, auth=auth)
>>> print(r.headers)
...
'x-ratelimit-remaining': '4995'
'x-ratelimit-limit': '5000'
...
優秀な。 あらゆる種類のエキサイティングな方法でGitHubAPIを悪用するPythonプログラムを、さらに4995回作成するときが来ました。
カスタム動詞
時々、何らかの理由で、上記でカバーされていないHTTP動詞の使用を許可したり、使用を要求したりするサーバーを使用している場合があります。 この一例は、一部のWEBDAVサーバーが使用するMKCOLメソッドです。 心配しないでください。これらは引き続きリクエストで使用できます。 これらは、組み込みの.request
メソッドを利用します。 例えば:
>>> r = requests.request('MKCOL', url, data=data)
>>> r.status_code
200 # Assuming your call was correct
これを利用して、サーバーで許可されている任意のメソッド動詞を利用できます。
リンクヘッダー
多くのHTTPAPIはリンクヘッダーを備えています。 これらは、APIをより自己記述的で発見しやすくします。
GitHubは、APIのページ付けにこれらを使用します。次に例を示します。
>>> url = 'https://api.github.com/users/kennethreitz/repos?page=1&per_page=10'
>>> r = requests.head(url=url)
>>> r.headers['link']
'<https://api.github.com/users/kennethreitz/repos?page=2&per_page=10>; rel="next", <https://api.github.com/users/kennethreitz/repos?page=6&per_page=10>; rel="last"'
リクエストはこれらのリンクヘッダーを自動的に解析し、簡単に使用できるようにします。
>>> r.links["next"]
{'url': 'https://api.github.com/users/kennethreitz/repos?page=2&per_page=10', 'rel': 'next'}
>>> r.links["last"]
{'url': 'https://api.github.com/users/kennethreitz/repos?page=7&per_page=10', 'rel': 'last'}
トランスポートアダプタ
v1.0.0以降、Requestsはモジュラー内部設計に移行しました。 これが行われた理由の一部は、元々ここで説明されたであるトランスポートアダプタを実装することでした。 トランスポートアダプタは、HTTPサービスの対話メソッドを定義するメカニズムを提供します。 特に、サービスごとの構成を適用できます。
リクエストには、単一のトランスポートアダプタ HTTPAdapter が付属しています。 このアダプタは、強力な urllib3 ライブラリを使用して、HTTPおよびHTTPSとのデフォルトのリクエストインタラクションを提供します。 リクエスト Session が初期化されるたびに、これらの1つがHTTP用の Session オブジェクトにアタッチされ、もう1つがHTTPS用にアタッチされます。
リクエストを使用すると、ユーザーは特定の機能を提供する独自のトランスポートアダプタを作成して使用できます。 作成されたトランスポートアダプタは、適用するWebサービスの指示とともにSessionオブジェクトにマウントできます。
>>> s = requests.Session()
>>> s.mount('https://github.com/', MyAdapter())
マウント呼び出しは、トランスポートアダプタの特定のインスタンスをプレフィックスに登録します。 マウントされると、URLが指定されたプレフィックスで始まるセッションを使用して行われたHTTPリクエストは、指定されたトランスポートアダプタを使用します。
トランスポートアダプタの実装の詳細の多くはこのドキュメントの範囲を超えていますが、単純なSSLのユースケースについて次の例を見てください。 それ以上に、 BaseAdapter のサブクラス化を検討するかもしれません。
例:特定のSSLバージョン
リクエストチームは、基盤となるライブラリ( urllib3 )でデフォルトのSSLバージョンを使用するという特定の選択を行いました。 通常はこれで問題ありませんが、デフォルトと互換性のないバージョンを使用するサービスエンドポイントに接続する必要がある場合があります。
これには、HTTPAdapterの既存の実装のほとんどを使用し、 urllib3 に渡されるパラメーター ssl_version を追加することでトランスポートアダプターを使用できます。 ライブラリに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)
ブロッキングまたは非ブロッキング?
デフォルトのトランスポートアダプタが設定されている場合、Requestsはいかなる種類の非ブロッキングIOも提供しません。 Response.content プロパティは、応答全体がダウンロードされるまでブロックされます。 より細かくする必要がある場合は、ライブラリのストリーミング機能(ストリーミングリクエストを参照)を使用すると、一度に少量の応答を取得できます。 ただし、これらの呼び出しは引き続きブロックされます。
ブロッキングIOの使用について懸念がある場合は、リクエストをPythonの非同期フレームワークの1つと組み合わせるプロジェクトがたくさんあります。 いくつかの優れた例は、 requests-threads 、 grequests 、 requests-futures 、および httpx です。
ヘッダーの順序
異常な状況では、順序付けられた方法でヘッダーを提供したい場合があります。 OrderedDict
をheaders
キーワード引数に渡すと、ヘッダーに順序が付けられます。 ただし、リクエストで使用されるデフォルトヘッダーの順序が優先されます。つまり、headers
キーワード引数でデフォルトヘッダーをオーバーライドすると、他のヘッダーと比較して順序が狂って表示される場合があります。そのキーワード引数で。
これが問題になる場合は、 Session をカスタムOrderedDict
に設定して、 Session オブジェクトにデフォルトのヘッダーを設定することを検討する必要があります。 その順序が常に優先されます。
タイムアウト
サーバーがタイムリーに応答しない場合に備えて、外部サーバーへのほとんどの要求にはタイムアウトを付加する必要があります。 デフォルトでは、タイムアウト値が明示的に設定されていない限り、リクエストはタイムアウトしません。 タイムアウトがないと、コードが数分以上ハングする可能性があります。
connect タイムアウトは、クライアントがソケットでリモートマシン( connect()に対応)への接続を確立するのをリクエストが待機する秒数です。 接続タイムアウトを3の倍数よりわずかに大きい値に設定することをお勧めします。これはデフォルトの TCPパケット再送信ウィンドウです。
クライアントがサーバーに接続してHTTP要求を送信すると、 read タイムアウトは、サーバーが応答を送信するのをクライアントが待機する秒数です。 (具体的には、クライアントがサーバーから送信されたの間バイトを待機する秒数です。 99.9 % o fの場合、これはサーバーが最初のバイトを送信するまでの時間です)。
次のように、タイムアウトに単一の値を指定した場合:
r = requests.get('https://github.com', timeout=5)
タイムアウト値は、connect
とread
の両方のタイムアウトに適用されます。 値を個別に設定する場合は、タプルを指定します。
r = requests.get('https://github.com', timeout=(3.05, 27))
リモートサーバーの速度が非常に遅い場合は、タイムアウト値としてNoneを渡してからコーヒーを1杯取得することで、Requestsに応答を永久に待つように指示できます。
r = requests.get('https://github.com', timeout=None)