При использовании requests
module, есть ли способ распечатать необработанный HTTP-запрос?
Мне не нужны только заголовки, мне нужна строка запроса, заголовки и распечатка контента. Можно ли увидеть, что в конечном итоге построено из запроса HTTP?
При использовании requests
module, есть ли способ распечатать необработанный HTTP-запрос?
Мне не нужны только заголовки, мне нужна строка запроса, заголовки и распечатка контента. Можно ли увидеть, что в конечном итоге построено из запроса HTTP?
Начиная с версии 1.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{}\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
поддерживают получение содержимого запроса напрямую, а АнтониоHerraizS ответ документы.
Невозможно получить истинное сырое содержимое запроса из 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
Еще лучшая идея - использовать библиотеку 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
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
Вот код, который делает то же самое, но с заголовками ответов:
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
Вы можете использовать requestb.in для проверки запросов, если ваши пост-данные не являются частными.