web-dev-qa-db-ja.com

Pythonのリクエスト - httpリクエスト全体を印刷する(raw)

requestsモジュール を使っている間に、生のHTTPリクエストを出力する方法はありますか?

ヘッダだけではなく、要求行、ヘッダ、およびコンテンツのプリントアウトが必要です。最終的にHTTPリクエストから構築されるものを見ることは可能ですか?

153
huggie

v1.2.3以降 Requestsは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{}\n{}\n\n{}'.format(
        '-----------START-----------',
        req.method + ' ' + req.url,
        '\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 - 低レベルクラス

158
AntonioHerraizS

注:この答えは時代遅れです。新しいバージョンのrequestsは、 AntonioHerraizSの答え documentsのように、要求コンテンツを直接取得することをサポートします。

headers のような、より高いレベルのオブジェクトのみを扱うので、リクエストの true rawコンテンツを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を使用して、最終的に生の要求 bodyが存在する場合はそれらを作成し、HTTPConnection.sendを使用して別々に送信します。 sendがついにソケットに到達します。

あなたが望むことをするためのフックがないので、最後の手段として、あなたは内容を得るためにhttplibをモンキーパッチすることができます。これは脆弱な解決策であり、httplibが変更された場合はそれを適応させる必要があるかもしれません。このソリューションを使ってソフトウェアを配布するつもりなら、純粋なpythonモジュールなので、システムの代わりにhttplibをパッケージ化することを考えるとよいでしょう。

悲しいかな、それ以上の苦労はせずに、解決策:

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
41
goncalopp

もっと良いアイディアは、request_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
30
Emil Stenström
import requests
response = requests.post('http://httpbin.org/post', data={'key1':'value1'})
print(response.request.body)
print(response.request.headers)

私はリクエストバージョン2.18.4とPython 3を使っています

30
Payman

これは同じものですが、応答ヘッダーを持つコードです。

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()

私はこれを探すのに多くの時間を費やしたので、誰かが必要とするならば、私はそれをここに残しておきます。

6
denself

リクエストをフォーマットするために次の関数を使います。これは@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
2
Ben