Python istekleri - tüm http isteği yazdır (ham)?


197

requestsModülü kullanırken, ham HTTP isteğini yazdırmanın bir yolu var mı?

Sadece üstbilgileri istemiyorum, istek satırını, üstbilgileri ve içerik çıktılarını istiyorum. Nihayetinde HTTP isteğinden ne inşa edildiğini görmek mümkün müdür?


9
Yanıtın değil, isteğin içeriğini soruyor
goncalopp

2
Bu iyi bir soru. Kaynağa baktığımızda, hazırlanan bir talebin ham içeriğini elde etmenin bir yolu yok gibi görünüyor ve sadece gönderildiğinde seri hale getiriliyor. Bu iyi bir özellik gibi görünüyor.
Tim Pierce

Ayrıca, wireshark'ı başlatabilir ve bu şekilde görebilirsiniz.
RickyA

@ qwrrty bunu bir requestsözellik olarak entegre etmek zor olurdu , çünkü yeniden yazma / baypas etme anlamına gelir urllib3ve httplib. Aşağıdaki yığın izine bakın
goncalopp

Bu benim için çalıştı - stackoverflow.com/questions/10588644/…
Ajay

Yanıtlar:


215

V1.2.3 İstekler PreparedRequest nesnesini eklediğinden beri . Belgelere göre "sunucuya gönderilecek tam baytları içerir".

Bunu, bir isteği güzel bir şekilde yazdırmak için kullanabilirsiniz, örneğin:

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)

hangi üretir:

-----------START-----------
POST http://stackoverflow.com/
Content-Length: 7
X-Custom: Test

a=1&b=2

Ardından asıl talebi şu yolla gönderebilirsiniz:

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

Bu bağlantılar mevcut en son belgelere yöneliktir, bu nedenle içerik olarak değişebilirler: Gelişmiş - Hazırlanan istekler ve API - Daha düşük seviye sınıfları


2
Bu benim maymun yama yöntemimden çok daha sağlam. Yükseltme requestsbasittir, bu yüzden bunun kabul edilen cevap olması gerektiğini düşünüyorum
goncalopp

70
Basit response = requests.post(...)(veya requests.getveya requests.putvb.) Yöntemleri kullanırsanız, aslında bu işlemi PreparedResponsegerçekleştirebilirsiniz response.request. Elle manipüle çalışmalarını kaydedebilir requests.Requestve requests.Sessionsize bir yanıt almadan önce ham http verilerine erişmek için gerekmiyorsa,.
Gershom

2
İyi cevap. Güncellemek isteyebileceğiniz bir şey, HTTP'deki satır sonlarının \ n \ değil \ r \ n olması gerektiğidir.
ltc

3
url'den hemen sonra HTTP protokolü sürüm kısmı ne olacak? 'HTTP / 1.1' gibi mi? güzel yazıcınızı kullanarak yazdırırken bulunmaz.
Sajuuk

1
CRLF kullanmak için güncellendi, çünkü RFC 2616'nın gerektirdiği şey ve çok katı ayrıştırıcılar için bir sorun olabilir
18'de nimish

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

Ben kullanıyorum istekleri sürüm 2.18.4 ve Python 3


44

Not: Bu cevap eski. AntonioHerraizS'in yanıt belgeleri requests olarak, istek içeriğini doğrudan almak için daha yeni destek sürümleri .

Yalnızca üstbilgiler ve yöntem türü gibi daha üst düzey nesnelerle ilgilendiğinden, isteğin gerçek ham içeriğini çıkarmak mümkün değildir . istekleri göndermek için kullanır , ancak ham verilerle de ilgilenmez - kullanır . İşte bir isteğin temsili yığın izlemesi:requestsrequestsurllib3urllib3 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)

httplibMakinelerin içinde görebiliyoruzHTTPConnection._send_request dolaylı kullanımlarını HTTPConnection._send_outputnihayet ham isteği oluşturur ve (varsa) vücudu ve kullanımları HTTPConnection.sendbunları ayrı ayrı göndermek için. sendSonunda sokete ulaşır.

İstediğiniz şeyi yapmak için kanca olmadığından, son çare olarak httplibiçeriği almak için maymun yaması yapabilirsiniz . Kırılgan bir çözümdür ve httplibdeğiştirilirse uyarlamanız gerekebilir . Bu çözümü kullanarak yazılım dağıtmak istiyorsanız httplib, saf bir python modülü olduğu için, sistemin sistemlerini kullanmak yerine ambalajlamayı düşünebilirsiniz .

Ne yazık ki, daha fazla uzatmadan, çözüm:

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

çıktı veren:

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

Merhaba goncalopp, eğer patch_send () prosedürünü 2. kez (2. bir istekten sonra) çağırırsam, o zaman verileri iki kez yazdırır (yani yukarıda gösterildiği gibi çıkışın 2 katı)? Yani, 3. bir talepte bulunursam, 3x kez basar ve böyle devam eder ... Sadece bir kere çıktıyı nasıl alacağınız hakkında bir fikriniz var mı? Şimdiden teşekkürler.
opstalj

@opstalj patch_sendiçe httplib
aktardıktan

40

Daha da iyi bir fikir, konsola yazdırmanız için hem istekleri hem de yanıtları dize olarak atabilen request_toolbelt kütüphanesini kullanmaktır. Yukarıdaki çözümün iyi işlemediği dosyalar ve kodlamalar ile tüm zor durumları ele alır.

Bu kadar kolay:

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

Kaynak: https://toolbelt.readthedocs.org/en/latest/dumputils.html

Basitçe şunu yazarak yükleyebilirsiniz:

pip install requests_toolbelt

2
Bu, isteği göndermeden dökümü yapmıyor gibi görünüyor.
Dobes Vandermeer

1
"TypeError: 'str' ve 'UUID' nesnelerini" çağrıdan birleştiremediğim için dump_all düzgün çalışmıyor gibi görünüyor.
rtaft

@rtaft: Lütfen bunu github deposunda bir hata olarak bildirin
Emil Stenström

Dökümü> ve <işaretleriyle yazdırır, gerçek isteğin bir parçası mıdır?
Jay

1
@Jay Görünüş için gerçek istek / yanıta eklenmiş gibi görünüyorlar ( github.com/requests/toolbelt/blob/master/requests_toolbelt/… ) ve request_prefix = b '{some_request_prefix}', response_prefix = b dump_all'a '{some_response_prefix}' ( github.com/requests/toolbelt/blob/master/requests_toolbelt/… )
Christian Reall-Fluharty

7

İşte aynı yapan ancak yanıt başlıklarına sahip bir kod:

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

Bunu aramak için çok zaman harcadım, bu yüzden birinin ihtiyacı varsa burada bırakıyorum.


4

İstekleri biçimlendirmek için aşağıdaki işlevi kullanıyorum. Vücuttaki JSON nesnelerini de oldukça basması ve isteğin tüm bölümlerini etiketlemesi dışında @AntonioHerraizS gibidir.

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

Ve yanıtı biçimlendirmek için benzer bir işlevi var:

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

requestsolay kanca denir (2.23 itibariyle aslında sadece responsekanca var). Kanca, etkili URL, üstbilgiler ve gövdeler dahil olmak üzere, istek yanıt çiftinin tüm verilerini yazdırmak için bir istek üzerine kullanılabilir:

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

Çalıştırırken yazdırır:

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

Sen değiştirmek isteyebilirsiniz res.textiçin res.contenttepki ikili ise.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.