पायथन अनुरोध - संपूर्ण http अनुरोध (कच्चा) प्रिंट करें?


197

requestsमॉड्यूल का उपयोग करते समय , कच्चे HTTP अनुरोध को प्रिंट करने का कोई तरीका है?

मुझे केवल हेडर नहीं चाहिए, मुझे अनुरोध पंक्ति, शीर्ष लेख और सामग्री प्रिंटआउट चाहिए। क्या यह देखना संभव है कि अंततः HTTP अनुरोध से क्या बनाया गया है?


9
@ रिकी वह अनुरोध की सामग्री के बारे में पूछ रहा है, न कि प्रतिक्रिया के बारे में
12

2
यह एक अच्छा सवाल है। स्रोत को देखने से, ऐसा नहीं लगता है कि किसी तैयार अनुरोध के कच्चे माल को प्राप्त करने का कोई तरीका है, और इसे भेजे जाने पर ही इसे सीरियल किया जाता है। ऐसा लगता है कि यह एक अच्छी सुविधा होगी।
टिम पियर्स

खैर, आप भी वायरशर्क शुरू कर सकते हैं और इसे इस तरह से देख सकते हैं।
रिका डे

@qwrrty इसे एक requestsसुविधा के रूप में एकीकृत करना मुश्किल होगा , क्योंकि इसका मतलब होगा फिर से लिखना / बाईपास करना urllib3और httplib। नीचे स्टैक ट्रेस देखें
goncalopp

यह मेरे लिए काम करता है - stackoverflow.com/questions/10588644/…
अजय

जवाबों:


213

V1.2.3 अनुरोधों के बाद से ReadyedRequest ऑब्जेक्ट जोड़ा गया। प्रलेखन के अनुसार "इसमें सटीक बाइट्स हैं जो सर्वर को भेजे जाएंगे"।

एक अनुरोध को प्रिंट करने के लिए इसका उपयोग कर सकता है, जैसे:

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)

ये लिंक नवीनतम प्रलेखन के लिए उपलब्ध हैं, इसलिए वे सामग्री में बदल सकते हैं: उन्नत - तैयार अनुरोध और एपीआई - निम्न स्तर की कक्षाएं


2
यह मेरे बंदर पैचिंग विधि की तुलना में बहुत अधिक मजबूत है। उन्नयन requestsसीधा है, इसलिए मुझे लगता है कि यह स्वीकृत जवाब बन जाना चाहिए
गॉनकॉल्प

69
यदि आप सरल response = requests.post(...)(या requests.getया requests.put, आदि) विधियों का उपयोग करते हैं, तो आप वास्तव में PreparedResponseथ्रू प्राप्त कर सकते हैं response.request। यह मैन्युअल रूप से हेरफेर के काम को बचा सकता है requests.Requestऔर requests.Session, यदि आपको प्रतिक्रिया प्राप्त करने से पहले कच्चे http डेटा तक पहुंचने की आवश्यकता नहीं है।
गेरशोम

2
अच्छा उत्तर। एक बात जिसे आप अपडेट करना चाहते हैं, वह यह है कि HTTP में लाइन ब्रेक \ n नहीं सिर्फ \ n होना चाहिए।
lcc

3
url के बाद HTTP प्रोटोकॉल संस्करण भाग के बारे में क्या? 'HTTP / 1.1' की तरह? जब आपके सुंदर प्रिंटर का उपयोग करके प्रिंट आउट नहीं मिलता है।
सजुक

1
CRLF का उपयोग करने के लिए अद्यतन किया गया है, क्योंकि RFC 2616 की आवश्यकता है, और यह बहुत सख्त पार्सर्स के लिए एक मुद्दा हो सकता है
nmish

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

मैं अनुरोध संस्करण 2.18.4 और पायथन 3 का उपयोग कर रहा हूं


44

नोट: यह उत्तर पुराना है। requests समर्थन के नए संस्करणों को सीधे एंटोनियोहेरिज़ के उत्तर दस्तावेजों के रूप में अनुरोध सामग्री मिल रही है

अनुरोध का सही कच्चा माल प्राप्त करना संभव नहीं है 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सिस्टम के उपयोग के बजाय पैकेजिंग पर विचार करना चाह सकते हैं , जो कि आसान है, क्योंकि यह एक शुद्ध अजगर मॉड्यूल है।

काश, आगे की हलचल के बिना, समाधान:

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

हाय goncalopp, अगर मैं एक दूसरी बार (सेकंड अनुरोध के बाद) पैच_सेंड () प्रक्रिया को 2 बार कॉल करता हूं, तो यह डेटा को दो बार प्रिंट करता है (इसलिए 2x आउटपुट जैसा आपने दिखाया है)? तो, अगर मैं एक 3 अनुरोध करता, यह इसे 3x बार और इतने पर मुद्रित करेगा ... किसी भी विचार कैसे केवल एक बार उत्पादन प्राप्त करने के लिए? अग्रिम में धन्यवाद।
अपस्टलज

@opstalj आपको patch_sendकई बार कॉल नहीं करना चाहिए , केवल एक बार आयात करने के बादhttplib
goncalopp

40

एक और भी बेहतर विचार है 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

2
यह अनुरोध भेजने के बिना डंप नहीं लगता है, हालांकि।
डोबेस वांडरमेर

1
डंप_ल्ल ठीक से काम नहीं करता है क्योंकि मुझे कॉल से "टाइपर्रर: कॉन्टेक्ट 'और' यूयूआईडी 'ऑब्जेक्ट नहीं मिल सकते हैं।
18

@ क्रेटा: कृपया इसे उनके गीथूब रिपॉजिटरी में बग के रूप में रिपोर्ट करें: github.com/sigmavirus24/requests-toolbelt/…
एमिल स्टेनस्ट्रॉम

यह डंप को> और <संकेतों से प्रिंट करता है, क्या वे वास्तविक अनुरोध का हिस्सा हैं?
Jay

1
@ जय ऐसा लगता है कि वे दिखावे के लिए वास्तविक अनुरोध / प्रतिक्रिया के लिए तैयार हैं ( github.com/requests/toolbelt/blob/master/requests_toolbelt/ ... ) b '{some_response_prefix}' to dip_all ( github.com/requests/toolbelt/blob/master/requests_toolbelt/… )
ईसाई Reall-Fluharty

7

यहां एक कोड है, जो समान बनाता है, लेकिन प्रतिक्रिया हेडर के साथ:

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

मैंने इसे खोजने में बहुत समय बिताया है, इसलिए मैं इसे छोड़ रहा हूं, अगर किसी को जरूरत है।


4

मैं निम्नलिखित फ़ंक्शन का उपयोग प्रारूप अनुरोधों के लिए करता हूं। यह @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

1

requestsतथाकथित घटना हुक का समर्थन करता है (2.23 के रूप में वास्तव में केवल responseहुक है)। हुक का उपयोग पूर्ण अनुरोध-प्रतिक्रिया जोड़ी के डेटा को प्रिंट करने के अनुरोध पर किया जा सकता है, जिसमें प्रभावी 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करता है, तो प्रतिक्रिया द्विआधारी है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.