¿Cómo se obtienen los encabezados predeterminados en una solicitud urllib2?

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

  •  03-07-2019
  •  | 
  •  

Pregunta

Tengo un cliente web Python que usa urllib2. Es bastante fácil agregar encabezados HTTP a mis solicitudes salientes. Simplemente creo un diccionario de los encabezados que quiero agregar y lo paso al inicializador de Solicitud.

Sin embargo, otro " estándar " Los encabezados HTTP se agregan a la solicitud, así como los personalizados que agrego explícitamente. Cuando huelo la solicitud usando Wireshark, veo encabezados además de los que agrego yo mismo. Mi pregunta es ¿cómo obtengo acceso a estos encabezados? Quiero registrar cada solicitud (incluido el conjunto completo de encabezados HTTP) y no puedo entender cómo.

¿algún puntero?

en pocas palabras: ¿Cómo obtengo todos los encabezados salientes de una solicitud HTTP creada por urllib2?

¿Fue útil?

Solución

Si desea ver la solicitud HTTP literal que se envía y, por lo tanto, ver hasta el último encabezado exactamente como se representa en el cable, puede decirle a urllib2 que use su propia versión de un HTTPHandler que imprime (o guarda, o lo que sea) la solicitud HTTP saliente.

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

El resultado de ejecutar este código es:

GET / HTTP/1.1
Accept-Encoding: identity
Host: www.google.com
Connection: close
User-Agent: Python-urllib/2.6

Otros consejos

La biblioteca urllib2 usa objetos OpenerDirector para manejar la apertura real. Afortunadamente, la biblioteca de Python proporciona valores predeterminados para que no tenga que hacerlo. Sin embargo, son estos objetos OpenerDirector los que están agregando los encabezados adicionales.

Para ver qué son después de que se haya enviado la solicitud (para que pueda iniciar sesión, por ejemplo):

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)

El unredirected_hdrs es donde los OpenerDirectors vuelcan sus encabezados adicionales. Simplemente mirando req.headers mostrará solo sus propios encabezados: la biblioteca los deja sin molestias.

Si necesita ver los encabezados antes de enviar la solicitud, deberá subclasificar el OpenerDirector para interceptar la transmisión.

Espero que ayude.

EDITAR: olvidé mencionar que, una vez que la solicitud se haya enviado, req.header_items () le dará una lista de tuplas de TODOS los encabezados, con los suyos y los añadidos. por el OpenerDirector. Debería haber mencionado esto primero, ya que es el más sencillo :-) Lo siento.

EDIT 2: después de su pregunta sobre un ejemplo para definir su propio controlador, aquí está la muestra que se me ocurrió. La preocupación en cualquier monkeying con la cadena de solicitud es que debemos asegurarnos de que el controlador sea seguro para múltiples solicitudes, por lo que me siento incómodo simplemente reemplazando la definición de putheader en la clase HTTPConnection directamente.

Lamentablemente, debido a que los componentes internos de HTTPConnection y AbstractHTTPHandler son muy internos, tenemos que reproducir gran parte del código de la biblioteca de Python para inyectar nuestro comportamiento personalizado. Suponiendo que no me he burlado a continuación y esto funciona tan bien como lo hizo en mis 5 minutos de prueba, tenga cuidado de volver a visitar este reemplazo si actualiza su versión de Python a un número de revisión (es decir: 2.5.x a 2.5.y o 2.5 a 2.6, etc.).

Por lo tanto, debo mencionar que estoy en Python 2.5.1. Si tiene 2.6 o, particularmente, 3.0, es posible que deba ajustar esto en consecuencia.

Avísame si esto no funciona. Me estoy divirtiendo muchísimo con esta pregunta:

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

¿Qué tal algo como esto?

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

Una solución de bajo nivel:

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

Ejemplo:

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

Otra solución, la bruja usó la idea de ¿Cómo se obtienen los encabezados predeterminados en una solicitud urllib2? Pero no copia el código de 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

EDITAR: actualizar la fuente

ver urllib2.py:do_request (línea 1044 (1067)) y urllib2.py:do_open (línea 1073) (línea 293) self.addheaders = [('User-agent', client_version)] (solo 'User-agent' agregado)

Me parece que está buscando los encabezados del objeto de respuesta, que incluyen Connection: close , etc. Estos encabezados viven en el objeto devuelto por urlopen. Llegar a ellos es bastante fácil:

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

req.headers es una instancia de httplib.HTTPMessage

Debería enviar los encabezados http predeterminados (según lo especificado por w3.org ) junto con los que especifiques. Puede usar una herramienta como WireShark si desea verlos en su totalidad.

Edición :

Si desea registrarlos, puede usar WinPcap para capturar paquetes enviados por aplicaciones específicas ( en tu caso, python). También puede especificar el tipo de paquetes y muchos otros detalles.

-John

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top