Как вы получаете заголовки по умолчанию в запросе 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). Вы также можете указать тип пакетов и множество других деталей.

-Джон