Как вы получаете заголовки по умолчанию в запросе urllib2? - PullRequest
15 голосов
/ 02 марта 2009

У меня есть веб-клиент Python, который использует urllib2. Достаточно просто добавить HTTP-заголовки к моим исходящим запросам. Я просто создаю словарь заголовков, которые хочу добавить, и передаю его инициализатору запроса.

Однако к запросу добавляются и другие «стандартные» заголовки HTTP, а также пользовательские, которые я явно добавляю. Когда я анализирую запрос с помощью Wireshark, я вижу заголовки помимо тех, которые добавляю сам. Мой вопрос: как мне получить доступ к этим заголовкам? Я хочу регистрировать каждый запрос (включая полный набор заголовков HTTP) и не могу понять, как.

какие-нибудь указатели?

в двух словах: как получить все исходящие заголовки из HTTP-запроса, созданного urllib2?

Ответы [ 8 ]

10 голосов
/ 27 октября 2010

Если вы хотите видеть буквенный 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
5 голосов
/ 02 марта 2009

Библиотека 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 для перехвата передачи.

Надеюсь, это поможет.

РЕДАКТИРОВАТЬ: я забыл упомянуть, что, как только запрос был отправлен, 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, вам может потребоваться изменить это соответствующим образом.

Пожалуйста, дайте мне знать, если это не сработает. У меня слишком много веселья с этим вопросом:

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')]
2 голосов
/ 09 августа 2011

Другое решение, использующее идею Как получить заголовки по умолчанию в запросе 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

РЕДАКТИРОВАТЬ: обновить источник

2 голосов
/ 09 августа 2011

Низкоуровневое решение:

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
2 голосов
/ 02 марта 2009

Как насчет этого:

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')
0 голосов
/ 27 октября 2010

Мне кажется, что вы ищете заголовки объекта ответа, которые включают Connection: close и т. Д. Эти заголовки живут в объекте, возвращаемом urlopen. Добраться до них достаточно просто:

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

req.headers является экземпляром httplib.HTTPMessage

0 голосов
/ 02 марта 2009

Он должен отправлять заголовки http по умолчанию (как указано w3.org ) вместе с указанными вами. Вы можете использовать такой инструмент, как WireShark , если вы хотите увидеть их полностью.

Edit:

Если вы хотите зарегистрировать их, вы можете использовать WinPcap для захвата пакетов, отправленных определенными приложениями (в вашем случае, python). Вы также можете указать тип пакетов и многое другое.

-John

0 голосов
/ 02 марта 2009

см. Urllib2.py:do_request (строка 1044 (1067)) и urllib2.py:do_open (строка 1073) (строка 293) self.addheaders = [('User-agent', client_version)] (добавлен только 'User-agent')

...