Come si ottengono le intestazioni predefinite in una richiesta urllib2?
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?
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