关于Python请求:Python请求 – 打印整个http请求(原始)?

Python requests - print entire http request (raw)?

使用requests模块时,有没有办法打印原始HTTP请求?

我不想只是标题,我想要请求行,标题和内容打印输出。 是否有可能看到最终由HTTP请求构造的内容?


由于v1.2.3请求添加了PreparedRequest对象。根据文档"它包含将发送到服务器的确切字节"。

可以使用它来打印请求,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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('{}
{}

{}



{}'
.format(
        '-----------START-----------',
        req.method + ' ' + req.url,
        '

'
.join('{}: {}'.format(k, v) for k, v in req.headers.items()),
        req.body,
    ))

pretty_print_POST(prepared)

产生:

1
2
3
4
5
6
-----------START-----------
POST http://stackoverflow.com/
Content-Length: 7
X-Custom: Test

a=1&b=2

然后你可以发送实际的请求:

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

这些链接是可用的最新文档,因此它们可能会更改内容:
高级 - 准备好的请求和API - 低级别课程


注意:这个答案已经过时了。较新版本的requests支持直接获取请求内容,如AntonioHerraizS的回答文档。

requests中获取请求的真实原始内容是不可能的,因为它只处理更高级别的对象,例如标题和方法类型。 requests使用urllib3发送请求,但urllib3也不处理原始数据 - 它使用httplib。这是请求的代表性堆栈跟踪:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-> 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模块。

唉,不用多说,解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
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")

产生输出:

1
2
3
4
5
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


1
2
3
4
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


更好的想法是使用requests_toolbelt库,它可以将请求和响应转储为字符串,供您打印到控制台。它使用上述解决方案无法处理的文件和编码来处理所有棘手的情况。

它就像这样简单:

1
2
3
4
5
6
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

您只需输入以下命令即可安装:

1
pip install requests_toolbelt


这是一个代码,它使相同,但带有响应头:

1
2
3
4
5
6
7
8
9
10
11
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对象,它标记了请求的所有部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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 = '
'
.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

我有一个类似的功能来格式化响应:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def format_response(resp):
   """Pretty-format 'requests.Response'"""
    headers = '
'
.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