Как вы получаете заголовки по умолчанию в запросе urllib2?
У меня есть веб-клиент Python, который использует urllib2. Достаточно просто добавить заголовки HTTP в мои исходящие запросы. Я просто создаю словарь заголовков, которые хочу добавить, и передаю их в инициализатор запроса.
Однако в запрос добавляются другие "стандартные" HTTP-заголовки, а также пользовательские, которые я явно добавляю. Когда я нюхаю запрос с помощью Wireshark, я вижу заголовки, кроме тех, которые я добавляю. Мой вопрос: как получить доступ к этим заголовкам? Я хочу регистрировать каждый запрос (включая полный набор HTTP-заголовков) и не могу понять, как это сделать.
любые указатели?
в двух словах: как получить все исходящие заголовки из HTTP-запроса, созданного urllib2?
Ответы
Ответ 1
Если вы хотите увидеть потоковый HTTP-запрос, который отправлен, и, следовательно, просмотреть каждый последний заголовок точно так же, как он представлен на проводе, вы можете сказать urllib2
использовать свою собственную версию HTTPHandler
, которая распечатывает (или сохраняет или что-то еще) исходящий HTTP-запрос.
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/')
Результатом запуска этого кода является:
GET / HTTP/1.1
Accept-Encoding: identity
Host: www.google.com
Connection: close
User-Agent: Python-urllib/2.6
Ответ 2
Библиотека urllib2 использует объекты OpenerDirector для обработки фактического открытия. К счастью, библиотека python предоставляет значения по умолчанию, поэтому вам не нужно. Тем не менее, эти объекты OpenerDirector добавляют дополнительные заголовки.
Чтобы узнать, что они после отправки запроса (чтобы вы могли его зарегистрировать, например):
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 - это то, где OpenerDirectors сбрасывает свои дополнительные заголовки. Просто глядя на req.headers
, вы увидите только ваши собственные заголовки - библиотека оставляет эти неудобства для вас.
Если вам нужно увидеть заголовки перед отправкой запроса, вам необходимо подклассифицировать OpenerDirector, чтобы перехватить передачу.
Надеюсь, что это поможет.
EDIT: Я забыл упомянуть, что после отправки запроса req.header_items()
вы получите список кортежей ВСЕХ заголовков, как с вашими, так и с добавленными OpenerDirector. Я должен был упомянуть об этом первым, так как это наиболее просто:-) Извините.
ИЗМЕНИТЬ 2: После вашего вопроса о примере для определения вашего собственного обработчика, вот образец, который я придумал. Озабоченность в любой обезьяне с цепочкой запросов состоит в том, что мы должны быть уверены, что обработчик безопасен для нескольких запросов, поэтому мне неудобно просто менять определение putheader непосредственно в классе HTTPConnection.
К сожалению, поскольку внутренности HTTPConnection и AbstractHTTPHandler являются очень внутренними, мы должны воспроизвести большую часть кода из библиотеки python, чтобы внедрить наше пользовательское поведение. Предполагая, что я не подходил ниже, и это работает так же хорошо, как и в моих 5-минутных тестах, будьте осторожны, чтобы пересмотреть это переопределение, если вы обновите версию Python до номера версии (то есть: 2.5.x до 2.5.y или От 2,5 до 2,6 и т.д.).
Поэтому я должен упомянуть, что я на Python 2.5.1. Если у вас есть 2.6 или, в частности, 3.0, вам может потребоваться соответствующим образом отрегулировать это.
Пожалуйста, дайте мне знать, если это не сработает. У меня есть waaaayyyy слишком много удовольствия с этим вопросом:
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')]
Ответ 3
Как насчет чего-то вроде этого:
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')
Ответ 4
Низкоуровневое решение:
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
Пример:
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
Ответ 5
Другое решение, ведьма использовала идею из Как вы получаете заголовки по умолчанию в запросе urllib2? Но не копирует код из 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
ИЗМЕНИТЬ: обновите источник
Ответ 6
см. urllib2.py:do_request (строка 1044 (1067)) и urllib2.py:do_open (строка 1073)
(строка 293) self.addheaders = [('User-agent', client_version)] (добавлен только "Пользовательский агент" )
Ответ 7
Мне кажется, что вы ищете заголовки объекта ответа, которые включают Connection: close
и т.д. Эти заголовки живут в объекте, возвращаемом urlopen. Получение у них достаточно просто:
from urllib2 import urlopen
req = urlopen("http://www.google.com")
print req.headers.headers
req.headers
является экземпляром httplib.HTTPMessage
Ответ 8
Он должен отправить заголовки http по умолчанию (как указано w3.org) вместе с теми, которые вы указали. Вы можете использовать инструмент, например WireShark, если вы хотите увидеть их целиком.
Edit:
Если вы хотите их зарегистрировать, вы можете использовать WinPcap для захвата пакетов, отправленных конкретными приложениями (в вашем случае, python). Вы также можете указать тип пакетов и множество других деталей.
-Джон