requests
モジュールを使用しているときに、生のHTTPリクエストを出力する方法はありますか?
ヘッダーだけでなく、リクエストライン、ヘッダー、コンテンツのプリントアウトも必要です。最終的にHTTPリクエストから何が構築されるかを確認することは可能ですか?
requests
ことはrewritting /バイパスを意味するであろうとして、機能urllib3
とhttplib
。以下のスタックトレースを参照してください
requests
モジュールを使用しているときに、生のHTTPリクエストを出力する方法はありますか?
ヘッダーだけでなく、リクエストライン、ヘッダー、コンテンツのプリントアウトも必要です。最終的にHTTPリクエストから何が構築されるかを確認することは可能ですか?
requests
ことはrewritting /バイパスを意味するであろうとして、機能urllib3
とhttplib
。以下のスタックトレースを参照してください
回答:
v1.2.3以降、リクエストはPreparedRequestオブジェクトを追加しました。ドキュメントによると、「サーバーに送信される正確なバイトが含まれています」。
これを使用して、次のようにリクエストをきれいに出力できます。
import requests
req = requests.Request('POST','http://stackoverflow.com',headers={'X-Custom':'Test'},data='a=1&b=2')
prepared = req.prepare()
def pretty_print_POST(req):
"""
At this point it is completely built and ready
to be fired; it is "prepared".
However pay attention at the formatting used in
this function because it is programmed to be pretty
printed and may differ from the actual request.
"""
print('{}\n{}\r\n{}\r\n\r\n{}'.format(
'-----------START-----------',
req.method + ' ' + req.url,
'\r\n'.join('{}: {}'.format(k, v) for k, v in req.headers.items()),
req.body,
))
pretty_print_POST(prepared)
生成されるもの:
-----------START-----------
POST http://stackoverflow.com/
Content-Length: 7
X-Custom: Test
a=1&b=2
次に、これを使用して実際のリクエストを送信できます。
s = requests.Session()
s.send(prepared)
これらのリンクは利用可能な最新のドキュメントへのリンクであるため、内容が変わる可能性があります。 詳細-準備されたリクエストとAPI-低レベルのクラス
requests
は簡単なので、これが受け入れられる答えになると思います
response = requests.post(...)
(またはrequests.get
or requests.put
、など)メソッドを使用する場合、実際にPreparedResponse
通過することができますresponse.request
。応答を受信する前に生のhttpデータにアクセスする必要がない場合は、requests.Request
とを手動で操作する作業を節約できますrequests.Session
。
注:この回答は古くなっています。AntonioHerraizSの回答ドキュメントのrequests
ように、リクエストのコンテンツを直接取得する新しいバージョンのサポート。
はヘッダーやメソッドタイプなどの上位レベルのオブジェクトしか処理しないため、からリクエストの真の生のコンテンツを取得することはできません。requests
requests
用途は、urllib3
リクエストを送信するが、それにurllib3
も生のデータを扱っていない-それは使用していますhttplib
。リクエストの代表的なスタックトレースは次のとおりです。
-> r= requests.get("http://google.com")
/usr/local/lib/python2.7/dist-packages/requests/api.py(55)get()
-> return request('get', url, **kwargs)
/usr/local/lib/python2.7/dist-packages/requests/api.py(44)request()
-> return session.request(method=method, url=url, **kwargs)
/usr/local/lib/python2.7/dist-packages/requests/sessions.py(382)request()
-> resp = self.send(prep, **send_kwargs)
/usr/local/lib/python2.7/dist-packages/requests/sessions.py(485)send()
-> r = adapter.send(request, **kwargs)
/usr/local/lib/python2.7/dist-packages/requests/adapters.py(324)send()
-> timeout=timeout
/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/connectionpool.py(478)urlopen()
-> body=body, headers=headers)
/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/connectionpool.py(285)_make_request()
-> conn.request(method, url, **httplib_request_kw)
/usr/lib/python2.7/httplib.py(958)request()
-> self._send_request(method, url, body, headers)
内部 httplib
機械の最終的に生のリクエストとボディ(存在する場合)を作成し、それらを個別に送信するために使用するHTTPConnection._send_request
間接的な使用を確認できます。最終的にソケットに到達します。HTTPConnection._send_output
HTTPConnection.send
send
やりたいことをするためのフックがないので、最後の手段として、パッチhttplib
を取得してコンテンツを取得できます。これは脆弱なソリューションであり、httplib
変更された場合はそれを適応させる必要があるかもしれません。このソリューションを使用してソフトウェアを配布する場合はhttplib
、システムを使用する代わりにパッケージ化を検討することをお勧めします。これは、純粋なPythonモジュールであるため、簡単です。
悲しいかな、それ以上の苦労なしに、ソリューション:
import requests
import httplib
def patch_send():
old_send= httplib.HTTPConnection.send
def new_send( self, data ):
print data
return old_send(self, data) #return is not necessary, but never hurts, in case the library is changed
httplib.HTTPConnection.send= new_send
patch_send()
requests.get("http://www.python.org")
出力を生成します:
GET / HTTP/1.1
Host: www.python.org
Accept-Encoding: gzip, deflate, compress
Accept: */*
User-Agent: python-requests/2.1.0 CPython/2.7.3 Linux/3.2.0-23-generic-pae
patch_send
インポート後に複数回呼び出すことはできません。一度だけ呼び出してくださいhttplib
さらに良いアイデアは、コンソールに出力するために、リクエストとレスポンスの両方を文字列としてダンプできるrequests_toolbeltライブラリを使用することです。上記のソリューションではうまく処理できないファイルやエンコーディングのすべてのトリッキーなケースを処理します。
それはこれと同じくらい簡単です:
import requests
from requests_toolbelt.utils import dump
resp = requests.get('https://httpbin.org/redirect/5')
data = dump.dump_all(resp)
print(data.decode('utf-8'))
出典:https : //toolbelt.readthedocs.org/en/latest/dumputils.html
次のように入力するだけでインストールできます。
pip install requests_toolbelt
同じコードを作成しますが、応答ヘッダーを使用しています。
import socket
def patch_requests():
old_readline = socket._fileobject.readline
if not hasattr(old_readline, 'patched'):
def new_readline(self, size=-1):
res = old_readline(self, size)
print res,
return res
new_readline.patched = True
socket._fileobject.readline = new_readline
patch_requests()
私はこれを探すのに多くの時間を費やしたので、誰かが必要な場合はここに残しておきます。
次の関数を使用してリクエストをフォーマットします。これは@AntonioHerraizSに似ていますが、本文にJSONオブジェクトをきれいに出力し、リクエストのすべての部分にラベルを付けます。
format_json = functools.partial(json.dumps, indent=2, sort_keys=True)
indent = functools.partial(textwrap.indent, prefix=' ')
def format_prepared_request(req):
"""Pretty-format 'requests.PreparedRequest'
Example:
res = requests.post(...)
print(format_prepared_request(res.request))
req = requests.Request(...)
req = req.prepare()
print(format_prepared_request(res.request))
"""
headers = '\n'.join(f'{k}: {v}' for k, v in req.headers.items())
content_type = req.headers.get('Content-Type', '')
if 'application/json' in content_type:
try:
body = format_json(json.loads(req.body))
except json.JSONDecodeError:
body = req.body
else:
body = req.body
s = textwrap.dedent("""
REQUEST
=======
endpoint: {method} {url}
headers:
{headers}
body:
{body}
=======
""").strip()
s = s.format(
method=req.method,
url=req.url,
headers=indent(headers),
body=indent(body),
)
return s
そして、私は応答をフォーマットする同様の関数を持っています:
def format_response(resp):
"""Pretty-format 'requests.Response'"""
headers = '\n'.join(f'{k}: {v}' for k, v in resp.headers.items())
content_type = resp.headers.get('Content-Type', '')
if 'application/json' in content_type:
try:
body = format_json(resp.json())
except json.JSONDecodeError:
body = resp.text
else:
body = resp.text
s = textwrap.dedent("""
RESPONSE
========
status_code: {status_code}
headers:
{headers}
body:
{body}
========
""").strip()
s = s.format(
status_code=resp.status_code,
headers=indent(headers),
body=indent(body),
)
return s
requests
いわゆるイベントフックをサポートします(2.23以降、実際にはresponse
フックのみです)。このフックをリクエストで使用して、次のような有効なURL、ヘッダー、本文を含む、リクエストとレスポンスのペアの完全なデータを出力できます。
import textwrap
import requests
def print_roundtrip(response, *args, **kwargs):
format_headers = lambda d: '\n'.join(f'{k}: {v}' for k, v in d.items())
print(textwrap.dedent('''
---------------- request ----------------
{req.method} {req.url}
{reqhdrs}
{req.body}
---------------- response ----------------
{res.status_code} {res.reason} {res.url}
{reshdrs}
{res.text}
''').format(
req=response.request,
res=response,
reqhdrs=format_headers(response.request.headers),
reshdrs=format_headers(response.headers),
))
requests.get('https://httpbin.org/', hooks={'response': print_roundtrip})
それを実行すると印刷されます:
---------------- request ----------------
GET https://httpbin.org/
User-Agent: python-requests/2.23.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
None
---------------- response ----------------
200 OK https://httpbin.org/
Date: Thu, 14 May 2020 17:16:13 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
<!DOCTYPE html>
<html lang="en">
...
</html>
あなたは、変更することもできますres.text
しres.content
、応答がバイナリである場合。