2009-03-02 23 views
14

Tengo un cliente web de Python que usa urllib2. Es bastante fácil agregar encabezados HTTP a mis solicitudes salientes. Simplemente creo un diccionario de los encabezados que deseo agregar y lo paso al inicializador de solicitudes.¿Cómo se obtienen los encabezados predeterminados en una solicitud de urllib2?

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

¿Alguna sugerencia?

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

Respuesta

10

Si quieres ver la petición HTTP literal que se envía, y por lo tanto ver cada último encabezado exactamente como se representa en el cable, luego puede indicar urllib2 para usar su propia versión de 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 
+3

Si se conecta a través de SSL, use 'urllib2.HTTPSHandler' (' https_open() ') y' httplib.HTTPSConnection' en su lugar. –

0

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)] (sólo ' User-agent 'agregado)

-1

Debe enviar los encabezados http predeterminados (según lo especificado por w3.org) junto con los que especifique. Puede utilizar una herramienta como WireShark si desea verlos en su totalidad.

Editar:

Si desea conectarse a ellos, puede utilizar WinPcap para capturar paquetes enviados por aplicaciones específicas (en su caso), pitón. También puede especificar el tipo de paquetes y muchos otros detalles.

-John

+0

Necesito iniciar sesión desde dentro de mi programa Python para que WinPcap no me ayude. gracias sin embargo. –

+0

Sí lo hará, ¿incluso ha leído qué es o cómo usarlo? Se usa con el programa wireshark, que muestra el resultado analizado de los paquetes y tiene la capacidad de registrarlos. –

+0

los paquetes contienen los encabezados, pensé que era obvio. Puede invocar/incorporar winpcap en su aplicación. –

5

La biblioteca urllib2 utiliza objetos OpenerDirector para manejar la abertura real. Afortunadamente, la biblioteca de Python proporciona los valores predeterminados para que no tenga que hacerlo. Sin embargo, son estos objetos OpenerDirector los que agregan los encabezados adicionales.

para ver lo que hay después de la solicitud ha sido enviado (por lo que puede 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) 

Los unredirected_hdrs es donde los OpenerDirectors vierten sus cabeceras extra. Simplemente al mirar req.headers, solo se mostrarán sus propios encabezados; la biblioteca los dejará sin molestar.

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 enviada la solicitud, req.header_items() le dará una lista de tuplas de TODOS los encabezados, con las suyas y las agregadas 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, esta es la muestra que se me ocurrió. La preocupación en cualquier monkeying con la cadena de solicitud es que tenemos que estar seguros de que el controlador es seguro para múltiples solicitudes, por lo que no me siento cómodo simplemente reemplazando la definición de putheader en la clase HTTPConnection directamente.

Lamentablemente, como las partes internas de HTTPConnection y AbstractHTTPHandler son muy internas, tenemos que reproducir gran parte del código de la biblioteca de python para inyectar nuestro comportamiento personalizado. Suponiendo que no me haya equivocado a continuación y esto funciona tan bien como lo hizo en mis 5 minutos de prueba, tenga cuidado de revisar esta anulación 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.).

Debo mencionar que estoy en Python 2.5.1. Si tiene 2.6 o, particularmente, 3.0, puede necesitar ajustar esto en consecuencia.

Háganme saber si esto no funciona. Estoy teniendo waaaayyyy demasiada diversión 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')] 
+0

esto es muy útil. sin embargo, no veo * todos * los encabezados aún (como, Conexión: Cerrar) –

+0

Hmmm .... ¿Te importaría publicar cómo estás construyendo la Solicitud y cómo estás abriendo la conexión, por favor? –

+0

abridor = urllib2.build_opener (urllib2.HTTPCookieProcessor (self.cookie_jar)) petición = urllib2.Request (url, Ninguno, encabezados) –

2

¿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') 
+0

esto es MUY cercano a lo que necesito. el único problema es cuando lo llamo en un bucle, sigue añadiendo encabezados de repetición. –

+0

JUSTUS, esto está tan cerca ... ¿puedes actualizar tu respuesta si tienes alguna otra idea? –

+0

No entiendo lo que quiere decir con "en un bucle". Pero, dado que esto requiere tanto hackeo, me pregunto por qué necesita tanta tala. Puede que sea mejor usar un proxy http, hacer todo el registro y usar urllib para hablar con él. – Justus

0

Me suena como que está en busca de las cabeceras del objeto respuesta, que incluyen Connection: close, etc. Estas cabeceras viven en el objeto devuelto por urlopen. Conseguir en 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

+0

no .. estaba buscando encabezados de solicitud, no encabezados de respuesta –

+0

Ah, bien entonces tendrá que crear su propio controlador para las solicitudes HTTP que vuelque esto como lo podrían hacer los ejemplos anteriores, o si está abierto a ajustar el stdlib , simplemente coloque una línea de registro en AbstractHTTPHandler.do_open que vuelque los encabezados. – dcolish

+0

La variable debería haberse escrito 'rep' ya que no responde y se debe utilizar el método documentado' .info() 'en lugar del atributo' headers' no documentado. –

2

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, respuesta.razón:

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

Una otra solución, bruja utiliza la idea de How do you get default headers in a urllib2 Request? 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: Actualice la fuente

Cuestiones relacionadas