在使用请求模块时,是否有办法打印原始HTTP请求?

我不仅仅需要标题,我还需要请求行、标题和内容打印输出。是否有可能看到最终从HTTP请求构造什么?


当前回答

import requests

response = requests.post('http://httpbin.org/post', data={'key1': 'value1'})
print(response.request.url)
print(response.request.body)
print(response.request.headers)

响应对象有一个.request属性,它是被发送的PreparedRequest对象。

其他回答

更好的想法是使用requests_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 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()

我花了很长时间找这个,所以我把它留在这里,如果有人需要的话。

import requests

response = requests.post('http://httpbin.org/post', data={'key1': 'value1'})
print(response.request.url)
print(response.request.body)
print(response.request.headers)

响应对象有一个.request属性,它是被发送的PreparedRequest对象。

因为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{}\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 -低级类

请求支持所谓的事件钩子(从2.23开始,实际上只有响应钩子)。钩子可以在请求上使用,打印完整的请求-响应对的数据,包括有效的URL,标题和主体,例如:

import textwrap
import requests

def print_roundtrip(response, *args, **kwargs):
    format_headers = lambda d: '\n'.join(f'{k}: {v}' for k, v in d.items())
    print(textwrap.dedent('''
        ---------------- request ----------------
        {req.method} {req.url}
        {reqhdrs}

        {req.body}
        ---------------- response ----------------
        {res.status_code} {res.reason} {res.url}
        {reshdrs}

        {res.text}
    ''').format(
        req=response.request, 
        res=response, 
        reqhdrs=format_headers(response.request.headers), 
        reshdrs=format_headers(response.headers), 
    ))

requests.get('https://httpbin.org/', hooks={'response': print_roundtrip})

运行它输出:

---------------- request ----------------
GET https://httpbin.org/
User-Agent: python-requests/2.23.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive

None
---------------- response ----------------
200 OK https://httpbin.org/
Date: Thu, 14 May 2020 17:16:13 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 9593
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

<!DOCTYPE html>
<html lang="en">
...
</html>

如果响应是二进制的,您可能希望将res.text更改为res.content。