web-dev-qa-db-de.com

Python-Anfragen - ganze http-Anfrage drucken (roh)?

Gibt es beim Drucken des Moduls requests eine Möglichkeit, die unformatierte HTTP-Anforderung zu drucken? 

Ich möchte nicht nur die Kopfzeilen, ich möchte die Anforderungszeile, die Kopfzeilen und den Inhaltsausdruck. Kann man sehen, was letztendlich aus einer HTTP-Anfrage besteht?

137
huggie

Since v1.2.3 Requests hat das PreparedRequest-Objekt hinzugefügt. Laut Dokumentation "enthält es die genauen Bytes, die an den Server gesendet werden".

Man kann das so benutzen, um eine Anfrage hübsch auszudrucken:

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)

was produziert:

-----------START-----------
POST http://stackoverflow.com/
Content-Length: 7
X-Custom: Test

a=1&b=2

Dann können Sie die eigentliche Anfrage mit diesem versenden:

s = requests.Session()
s.send(prepared)

Diese Links beziehen sich auf die aktuellste verfügbare Dokumentation und können sich daher im Inhalt ändern: Erweitert - Vorbereitete Anforderungen und API - Untergeordnete Klassen

147
AntonioHerraizS

Hinweis: Diese Antwort ist veraltet. Neuere Versionen von requests unterstützen das direkte Abrufen des Anforderungsinhalts als Antwort von AntonioHerraizS documents.

Es ist nicht möglich, den true -Rohinhalt der Anforderung aus requests zu holen, da er nur Objekte höherer Ebene behandelt, z. B. Header und Methodentyp. requests verwendet urllib3 zum Senden von Anforderungen, aber urllib3auch behandelt keine Rohdaten - es verwendet httplib. Hier ist ein repräsentativer Stack-Trace einer Anfrage:

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

In der httplib-Maschinerie können wir sehen, dass HTTPConnection._send_request indirekt HTTPConnection._send_output verwendet, der schließlich die Rohanforderung und body (falls vorhanden) erstellt und sie mit HTTPConnection.send separat sendet. send erreicht schließlich den Sockel.

Da es keine Haken gibt, um das zu tun, was Sie wollen, können Sie als letzten Ausweg httplib einen Affen-Patch erstellen, um den Inhalt zu erhalten. Es ist eine fragile Lösung, die Sie möglicherweise anpassen müssen, wenn httplib geändert wird. Wenn Sie beabsichtigen, Software mit dieser Lösung zu vertreiben, sollten Sie das Paket httplib anstelle der Verwendung des Systems in Betracht ziehen. Dies ist einfach, da es sich um ein reines Python-Modul handelt.

Leider ohne weiteres die Lösung:

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

was ergibt die Ausgabe:

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

Eine noch bessere Idee ist die Verwendung der Bibliothek "request_toolbelt", die sowohl Anforderungen als auch Antworten als Zeichenfolgen für den Ausdruck an die Konsole ausgibt. Es behandelt alle heiklen Fälle mit Dateien und Kodierungen, mit denen die oben genannte Lösung nicht gut umgehen kann.

So einfach geht's:

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

Quelle: https://toolbelt.readthedocs.org/de/latest/dumputils.html

Sie können es einfach installieren, indem Sie Folgendes eingeben:

pip install requests_toolbelt
28
Emil Stenström
import requests
response = requests.post('http://httpbin.org/post', data={'key1':'value1'})
print(response.request.body)
print(response.request.headers)

Ich benutze request version 2.18.4 und Python 3

19
Payman

Hier ist ein Code, der dasselbe macht, jedoch mit Antwort-Headern: 

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

Ich habe viel Zeit damit verbracht, das zu suchen, also lasse ich es hier, wenn jemand etwas braucht.

4
denself

Ich benutze die folgende Funktion, um Anfragen zu formatieren. Es ist wie bei @AntonioHerraizS, nur dass JSON-Objekte auch im Hauptteil hübsch gedruckt werden und alle Teile der Anforderung beschriftet werden.

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

Und ich habe eine ähnliche Funktion, um die Antwort zu formatieren:

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
0
Ben