Как получить заголовки по умолчанию в запросе urllib2?

StackOverflow https://stackoverflow.com/questions/603856

  •  03-07-2019
  •  | 
  •  

Вопрос

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

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

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

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

Это было полезно?

Решение

Если вы хотите увидеть отправленный буквальный 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

Другие советы

Библиотека 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)

Undirected_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')]

Как насчет чего-то вроде этого:

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')

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

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()

ответ.статус, ответ.причина:

1: 200 OK

ответ.request_headers:

[('Host', 'www.python.org'), ('Accept-Encoding', 'identity'), ('Referer', '/'), ('User-agent', 'test')]

ответ.заголовок_запроса:

GET /index.html HTTP/1.1
Host: www.python.org
Accept-Encoding: identity
Referer: /
User-agent: test

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

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

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

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

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

req.headers является примером httplib.HTTPMessage

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

Редактировать:

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

-Джон

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top