Domanda

Ho un client Web Python che utilizza urllib2. È abbastanza facile aggiungere intestazioni HTTP alle mie richieste in uscita. Devo solo creare un dizionario delle intestazioni che voglio aggiungere e passarlo all'inizializzatore della richiesta.

Tuttavia, altri "standard" Le intestazioni HTTP vengono aggiunte alla richiesta e quelle personalizzate che aggiungo esplicitamente. Quando annuso la richiesta usando Wireshark, vedo delle intestazioni oltre a quelle che mi aggiungo. La mia domanda è: come posso accedere a queste intestazioni? Voglio registrare ogni richiesta (incluso il set completo di intestazioni HTTP) e non riesco a capire come.

qualche puntatore?

in breve: come posso ottenere tutte le intestazioni in uscita da una richiesta HTTP creata da urllib2?

È stato utile?

Soluzione

Se vuoi vedere la richiesta HTTP letterale che viene inviata, e quindi vedere ogni ultima intestazione esattamente come è rappresentata sul filo, allora puoi dire a urllib2 di usare la tua versione di un HTTPHandler che stampa (o salva, o qualsiasi altra cosa) la richiesta HTTP in uscita.

import httplib, urllib2

class MyHTTPConnection(httplib.HTTPConnection):
    def send(self, s):
        print s  # or save them, or whatever!
        httplib.HTTPConnection.send(self, s)

class MyHTTPHandler(urllib2.HTTPHandler):
    def http_open(self, req):
        return self.do_open(MyHTTPConnection, req)

opener = urllib2.build_opener(MyHTTPHandler)
response = opener.open('http://www.google.com/')

Il risultato dell'esecuzione di questo codice è:

GET / HTTP/1.1
Accept-Encoding: identity
Host: www.google.com
Connection: close
User-Agent: Python-urllib/2.6

Altri suggerimenti

La libreria urllib2 utilizza oggetti OpenerDirector per gestire l'apertura effettiva. Fortunatamente, la libreria python fornisce le impostazioni predefinite, quindi non è necessario. Sono, tuttavia, questi oggetti OpenerDirector che stanno aggiungendo le intestazioni extra.

Per vedere quali sono dopo l'invio della richiesta (in modo che sia possibile registrarla, ad esempio):

req = urllib2.Request(url='http://google.com')
response = urllib2.urlopen(req)
print req.unredirected_hdrs

(produces {'Host': 'google.com', 'User-agent': 'Python-urllib/2.5'} etc)

Unredirected_hdrs è il punto in cui gli OpenerDirector scaricano le loro intestazioni extra. Basta guardare req.headers per mostrare solo le tue intestazioni: la libreria lascia quelle non molestate per te.

Se è necessario visualizzare le intestazioni prima di inviare la richiesta, è necessario sottoclassare OpenerDirector per intercettare la trasmissione.

Spero che sia d'aiuto.

EDIT: ho dimenticato di menzionare che, una volta che la richiesta è stata inviata, req.header_items () ti fornirà un elenco di tuple di TUTTE le intestazioni, con le tue e quelle aggiunte da OpenerDirector. Avrei dovuto menzionarlo prima poiché è il più semplice :-) Scusa.

MODIFICA 2: Dopo la tua domanda su un esempio per la definizione del tuo gestore, ecco l'esempio che ho trovato. La preoccupazione in ogni monkeying con la catena di richieste è che dobbiamo essere sicuri che il gestore sia sicuro per più richieste, motivo per cui mi sento a disagio a sostituire direttamente la definizione di putheader sulla classe HTTPConnection.

Purtroppo, poiché gli interni di HTTPConnection e AbstractHTTPHandler sono molto interni, dobbiamo riprodurre gran parte del codice dalla libreria python per iniettare il nostro comportamento personalizzato. Supponendo che non abbia preso in giro di seguito e che funzioni così come nei miei 5 minuti di test, si prega di fare attenzione a rivedere questa sostituzione se si aggiorna la versione di Python a un numero di revisione (es. Da 2.5.xa 2.5.y o Da 2,5 a 2,6, ecc.)

Dovrei quindi menzionare che sono su Python 2.5.1. Se hai 2.6 o, in particolare, 3.0, potresti doverlo modificare di conseguenza.

Per favore fatemi sapere se non funziona. Mi sto divertendo troppo con questa domanda:

import urllib2
import httplib
import socket


class CustomHTTPConnection(httplib.HTTPConnection):

    def __init__(self, *args, **kwargs):
        httplib.HTTPConnection.__init__(self, *args, **kwargs)
        self.stored_headers = []

    def putheader(self, header, value):
        self.stored_headers.append((header, value))
        httplib.HTTPConnection.putheader(self, header, value)


class HTTPCaptureHeaderHandler(urllib2.AbstractHTTPHandler):

    def http_open(self, req):
        return self.do_open(CustomHTTPConnection, req)

    http_request = urllib2.AbstractHTTPHandler.do_request_

    def do_open(self, http_class, req):
        # All code here lifted directly from the python library
        host = req.get_host()
        if not host:
            raise URLError('no host given')

        h = http_class(host) # will parse host:port
        h.set_debuglevel(self._debuglevel)

        headers = dict(req.headers)
        headers.update(req.unredirected_hdrs)
        headers["Connection"] = "close"
        headers = dict(
            (name.title(), val) for name, val in headers.items())
        try:
            h.request(req.get_method(), req.get_selector(), req.data, headers)
            r = h.getresponse()
        except socket.error, err: # XXX what error?
            raise urllib2.URLError(err)
        r.recv = r.read
        fp = socket._fileobject(r, close=True)

        resp = urllib2.addinfourl(fp, r.msg, req.get_full_url())
        resp.code = r.status
        resp.msg = r.reason

        # This is the line we're adding
        req.all_sent_headers = h.stored_headers
        return resp

my_handler = HTTPCaptureHeaderHandler()
opener = urllib2.OpenerDirector()
opener.add_handler(my_handler)
req = urllib2.Request(url='http://www.google.com')

resp = opener.open(req)

print req.all_sent_headers

shows: [('Accept-Encoding', 'identity'), ('Host', 'www.google.com'), ('Connection', 'close'), ('User-Agent', 'Python-urllib/2.5')]

Che ne dici di qualcosa del genere:

import urllib2
import httplib

old_putheader = httplib.HTTPConnection.putheader
def putheader(self, header, value):
    print header, value
    old_putheader(self, header, value)
httplib.HTTPConnection.putheader = putheader

urllib2.urlopen('http://www.google.com')

Una soluzione di basso livello:

import httplib

class HTTPConnection2(httplib.HTTPConnection):
    def __init__(self, *args, **kwargs):
        httplib.HTTPConnection.__init__(self, *args, **kwargs)
        self._request_headers = []
        self._request_header = None

    def putheader(self, header, value):
        self._request_headers.append((header, value))
        httplib.HTTPConnection.putheader(self, header, value)

    def send(self, s):
        self._request_header = s
        httplib.HTTPConnection.send(self, s)

    def getresponse(self, *args, **kwargs):
        response = httplib.HTTPConnection.getresponse(self, *args, **kwargs)
        response.request_headers = self._request_headers
        response.request_header = self._request_header
        return response

Esempio:

conn = HTTPConnection2("www.python.org")
conn.request("GET", "/index.html", headers={
    "User-agent": "test",
    "Referer": "/",
})
response = conn.getresponse()

response.status, response.reason:

1: 200 OK

response.request_headers:

[('Host', 'www.python.org'), ('Accept-Encoding', 'identity'), ('Referer', '/'), ('User-agent', 'test')]

response.request_header:

GET /index.html HTTP/1.1
Host: www.python.org
Accept-Encoding: identity
Referer: /
User-agent: test

Un'altra soluzione, strega ha usato l'idea di Come si ottengono le intestazioni predefinite in una richiesta urllib2? Ma non si copia il codice da std-lib:

class HTTPConnection2(httplib.HTTPConnection):
    """
    Like httplib.HTTPConnection but stores the request headers.
    Used in HTTPConnection3(), see below.
    """
    def __init__(self, *args, **kwargs):
        httplib.HTTPConnection.__init__(self, *args, **kwargs)
        self.request_headers = []
        self.request_header = ""

    def putheader(self, header, value):
        self.request_headers.append((header, value))
        httplib.HTTPConnection.putheader(self, header, value)

    def send(self, s):
        self.request_header = s
        httplib.HTTPConnection.send(self, s)


class HTTPConnection3(object):
    """
    Wrapper around HTTPConnection2
    Used in HTTPHandler2(), see below.
    """
    def __call__(self, *args, **kwargs):
        """
        instance made in urllib2.HTTPHandler.do_open()
        """
        self._conn = HTTPConnection2(*args, **kwargs)
        self.request_headers = self._conn.request_headers
        self.request_header = self._conn.request_header
        return self

    def __getattribute__(self, name):
        """
        Redirect attribute access to the local HTTPConnection() instance.
        """
        if name == "_conn":
            return object.__getattribute__(self, name)
        else:
            return getattr(self._conn, name)


class HTTPHandler2(urllib2.HTTPHandler):
    """
    A HTTPHandler which stores the request headers.
    Used HTTPConnection3, see above.

    >>> opener = urllib2.build_opener(HTTPHandler2)
    >>> opener.addheaders = [("User-agent", "Python test")]
    >>> response = opener.open('http://www.python.org/')

    Get the request headers as a list build with HTTPConnection.putheader():
    >>> response.request_headers
    [('Accept-Encoding', 'identity'), ('Host', 'www.python.org'), ('Connection', 'close'), ('User-Agent', 'Python test')]

    >>> response.request_header
    'GET / HTTP/1.1\\r\\nAccept-Encoding: identity\\r\\nHost: www.python.org\\r\\nConnection: close\\r\\nUser-Agent: Python test\\r\\n\\r\\n'
    """
    def http_open(self, req):
        conn_instance = HTTPConnection3()
        response = self.do_open(conn_instance, req)
        response.request_headers = conn_instance.request_headers
        response.request_header = conn_instance.request_header
        return response

EDIT: aggiorna l'origine

vedi urllib2.py:do_request (linea 1044 (1067)) e urllib2.py:do_open (linea 1073) (riga 293) self.addheaders = [('User-agent', client_version)] (solo 'User-agent' aggiunto)

Mi sembra che tu stia cercando le intestazioni dell'oggetto risposta, che includono Connessione: chiudi , ecc. Queste intestazioni vivono nell'oggetto restituito da urlopen. Raggiungerli è abbastanza facile:

from urllib2 import urlopen
req = urlopen("http://www.google.com")
print req.headers.headers

req.headers è un'istanza di httplib.HTTPMessage

Dovrebbe inviare le intestazioni http predefinite (come specificato da w3.org ) accanto a quelli specificati. Puoi utilizzare uno strumento come WireShark se desideri vederli nella loro interezza.

Modifica

Se desideri registrarli, puoi utilizzare WinPcap per acquisire pacchetti inviati da applicazioni specifiche ( nel tuo caso, pitone). Puoi anche specificare il tipo di pacchetti e molti altri dettagli.

-John

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top