Lorsque vous utilisez le module requests
, existe-t-il un moyen d'imprimer la requête HTTP brute?
Je ne veux pas que les en-têtes, je veux la ligne de demande, les en-têtes et l'impression du contenu. Est-il possible de voir ce qui est finalement construit à partir d'une requête HTTP?
Depuis v1.2.3 Les requêtes ont ajouté l'objet PreparedRequest. Selon la documentation "il contient les octets exacts qui seront envoyés au serveur".
On peut l'utiliser pour imprimer une requête, comme ceci:
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)
qui produit:
-----------START-----------
POST http://stackoverflow.com/
Content-Length: 7
X-Custom: Test
a=1&b=2
Ensuite, vous pouvez envoyer la demande réelle avec ceci:
s = requests.Session()
s.send(prepared)
Ces liens renvoient à la dernière documentation disponible, leur contenu pourrait donc en changer: Avancé - Demandes préparées et API - Classes de niveau inférieur
Note: cette réponse est obsolète. Les versions plus récentes de requests
permettent de récupérer le contenu de la demande directement, sous la forme de AntonioHerraizS answer documents.
Il est impossible d'extraire le contenu brut de la requête true de requests
, car il ne traite que des objets de niveau supérieur, tels que en-têtes et type de méthode. requests
utilise urllib3
pour envoyer des demandes, mais urllib3
aussi _ ne traite pas les données brutes - il utilise httplib
. Voici une trace de pile représentative d'une demande:
-> 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)
À l'intérieur de la machine httplib
, nous pouvons voir que HTTPConnection._send_request
utilise indirectement HTTPConnection._send_output
, ce qui crée finalement la requête brute et corps (s'il existe), et utilise HTTPConnection.send
pour les envoyer séparément. send
atteint finalement le socket.
Puisqu'il n'y a pas de crochets pour faire ce que vous voulez, en dernier recours, vous pouvez utiliser le correctif httplib
pour récupérer le contenu. C'est une solution fragile, et vous devrez peut-être l'adapter si vous changez httplib
. Si vous avez l’intention de distribuer des logiciels à l’aide de cette solution, envisagez d’empaqueter httplib
au lieu d’utiliser le système, ce qui est simple, car c’est un module python pur.
Hélas, sans plus tarder, la solution:
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")
qui donne la sortie:
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
Une meilleure idée consiste à utiliser la bibliothèque tasks_toolbelt, qui peut générer des requêtes et des réponses sous forme de chaînes à imprimer sur la console. Il gère tous les cas difficiles de fichiers et d’encodages que la solution ci-dessus ne gère pas bien.
C'est aussi simple que ça:
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'))
Source: https://toolbelt.readthedocs.org/en/latest/dumputils.html
Vous pouvez simplement l'installer en tapant:
pip install requests_toolbelt
import requests
response = requests.post('http://httpbin.org/post', data={'key1':'value1'})
print(response.request.body)
print(response.request.headers)
J'utilise requêtes version 2.18.4 et Python 3
Voici un code qui fait la même chose, mais avec les en-têtes de réponse:
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()
J'ai passé beaucoup de temps à chercher cela, alors je le laisse ici, si quelqu'un en a besoin.
J'utilise la fonction suivante pour formater les requêtes. Cela ressemble à @AntonioHerraizS, sauf que les objets JSON sont également imprimés joliment dans le corps et qu'il étiquette toutes les parties de la demande.
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
Et j'ai une fonction similaire pour formater la réponse:
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