Wie Sie Standard Header in einem urllib2 Anfrage bekommen?
Frage
Ich habe einen Python-Web-Client, den urllib2 verwendet. Es ist leicht genug, um HTTP-Header auf meine ausgehenden Anfragen hinzuzufügen. Ich schaffe nur ein Wörterbuch der Header ich hinzufügen möchte, und es dem Antrag initializer passieren.
Aber auch andere „Standard“ HTTP-Header der Anforderung hinzugefügt bekommen sowie die individuelle, die ich ausdrücklich hinzufügen. Als ich die Anfrage mit Wireshark schnüffeln, sehe ich Header neben denen, die ich mir selbst hinzufügen. Meine Frage ist, wie ein ich Zugang zu diesen Header bekommen? Ich mag jede Anfrage (einschließlich der voll Satz von HTTP-Header) protokollieren, und kann nicht herausfinden, wie.
alle Hinweise?
auf den Punkt:? Wie bekomme ich alle abgehenden Header einer HTTP-Anforderung von urllib2 erstellt
Lösung
Wenn Sie die wörtliche HTTP-Anforderung, um sehen möchten, die ausgesendet wird, und deshalb jeden letzten Header genau sehen, wie sie auf dem Draht dargestellt wird, dann können Sie urllib2
sagen Sie Ihre eigene Version eines HTTPHandler
zu verwenden, druckt (oder spart, oder was auch immer), um die ausgehende HTTP-Anfrage.
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/')
Das Ergebnis dieser Code ausgeführt wird:
GET / HTTP/1.1
Accept-Encoding: identity
Host: www.google.com
Connection: close
User-Agent: Python-urllib/2.6
Andere Tipps
Die urllib2 Bibliothek verwendet OpenerDirector Objekte die tatsächliche Öffnung zu handhaben. Glücklicherweise bietet die Python-Bibliothek standardmäßig so dass Sie nicht haben. Es ist jedoch diese OpenerDirector Objekte, die die zusätzlichen Header hinzufügen.
Um zu sehen, was sie sind, nachdem die Anforderung gesendet wurde (so dass Sie es anmelden können, zum Beispiel):
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)
Die unredirected_hdrs ist, wo der OpenerDirectors ihre zusätzlichen Header-Dump. Einfach bei req.headers
suchen nur Ihre eigenen Header zeigen -. Die Bibliothek jene unbehelligt für Sie verlässt
Wenn Sie die Header sehen müssen, bevor Sie die Anfrage zu senden, müssen Sie den OpenerDirector, um eine Unterklasse die Übertragung abzufangen.
Ich hoffe, das hilft.
EDIT: Ich vergaß zu erwähnen, dass, sobald die Anforderung als gesendet wurde, req.header_items()
wird Ihnen eine Liste von Tupeln von alle Header, sowohl mit Ihren eigenen und den von der OpenerDirector hinzugekommenen. Ich sollte das erste erwähnt haben, da es die einfachste :-) Leider ist.
EDIT 2: Nach dem Sie Ihre Frage ein Beispiel für Ihre eigenen Handler definieren, hier ist die Probe kam ich mit. Die Sorge in jedem nachäffen mit der Bitte Kette ist, dass wir müssen sicher sein, dass der Handler für mehrere Anforderungen sicher ist, weshalb ich unbequem nur direkt die Definition von putheader auf der Httpconnection-Klasse ersetzt wird.
Leider, denn die Interna von Httpconnection und den AbstractHTTPHandler sehr intern sind, haben wir viel von dem Code aus der Python-Bibliothek zu reproduzieren unser eigenes Verhalten zu injizieren. Angenommen, ich habe unten nicht gepatzt und das funktioniert so gut, wie es in meiner 5-Minuten-Tests haben, seien Sie bitte vorsichtig sein, diese Überschreibung zu überdenken, wenn Sie Ihre Python-Version auf eine Versionsnummer aktualisieren (dh: 2.5.x 2.5.y oder 2,5 bis 2,6, etc).
Ich soll deshalb erwähnen, dass ich bin auf Python 2.5.1. Wenn Sie 2.6 oder, besonders haben, 3.0, müssen Sie diese entsprechend anzupassen.
Bitte lassen Sie mich wissen, wenn dies nicht funktioniert. Ich waaaayyyy zu viel Spaß mit dieser Frage mit:
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')]
Wie wäre es etwa so:
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')
Eine Low-Level-Lösung:
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
Beispiel:
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
Eine andere Lösung, verwendet Hexe die Idee von Wie Sie Standard Header in einem urllib2 Anfrage erhalten Aber nicht kopiert Code aus 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: die Quelle Aktualisieren
siehe urllib2.py:do_request (Linie 1044 (1067)) und urllib2.py:do_open (Linie 1073) (Linie 293) self.addheaders = [( 'User-agent', client_version)] (nur 'User-agent' hinzugefügt)
Es klingt für mich wie Sie für die Header der Antwort Objekt suchen, die Connection: close
umfassen usw. Diese Header in dem Objekt durch urlopen zurück leben. bei ihnen zu bekommen ist einfach genug:
from urllib2 import urlopen
req = urlopen("http://www.google.com")
print req.headers.headers
req.headers
ist eine Instanz von httplib.HTTPMessage
Es sollte die Standard-HTTP-Header (wie angegeben von w3.org
Edit:
Wenn Sie sie anmelden möchten, können Sie WinPcap Pakete zu erfassen, indem spezifische Anwendungen gesendet ( in Ihrem Fall, python). Sie können auch die Art der Pakete und viele andere Details angeben.
-Johannes