Pergunta

Eu tenho um cliente web Python que usa urllib2. É bastante fácil de adicionar cabeçalhos HTTP aos meus pedidos de saída. Eu só criar um dicionário dos cabeçalhos que deseja adicionar, e passá-lo para o Pedido de initializer.

No entanto, outro "padrão" HTTP cabeçalhos são adicionados à solicitação, bem como os personalizados I explicitamente adicionar. Quando eu cheirar a solicitação usando Wireshark, vejo cabeçalhos além de os que eu me adicionar. A minha pergunta é como é que um eu tenha acesso a estes cabeçalhos? Quero registrar cada pedido (incluindo o completo conjunto de cabeçalhos HTTP), e não consegue descobrir como.

qualquer ponteiros?

Em poucas palavras:? Como faço para obter todos os cabeçalhos de saída a partir de uma solicitação HTTP criado por urllib2

Foi útil?

Solução

Se você quiser ver o pedido HTTP literal que é enviado para fora, e, portanto, ver cada último cabeçalho exatamente como ele é representado no fio, então você pode dizer urllib2 usar sua própria versão de um HTTPHandler que imprime (ou salva, ou qualquer outro) a solicitação HTTP de saída.

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

O resultado da execução deste código é:

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

Outras dicas

A biblioteca urllib2 usa OpenerDirector objetos para lidar com a abertura real. Felizmente, a biblioteca python fornece padrões para que você não precisa. É, no entanto, estes OpenerDirector objetos que estão adicionando os cabeçalhos extra.

Para ver o que eles são depois que a solicitação foi enviada (de modo que você pode registrá-lo, por exemplo):

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)

Os unredirected_hdrs é onde os OpenerDirectors despejar seus cabeçalhos extra. Basta olhar para req.headers irá mostrar apenas seus próprios cabeçalhos -. As folhas de biblioteca aqueles sem serem molestados para você

Se você precisa ver os cabeçalhos antes de enviar o pedido, você precisará subclasse o OpenerDirector, a fim de interceptar a transmissão.

Espero que ajude.

EDIT: eu esqueci de mencionar que, uma vez o pedido como foi enviada, req.header_items() vai lhe dar uma lista de tuplas de todos os cabeçalhos, tanto com o seu próprio e aqueles adicionados pelo OpenerDirector. Eu deveria ter mencionado este primeiro, já que é o mais simples :-) Desculpe.

EDIT 2: Após sua pergunta sobre um exemplo para definir o seu próprio manipulador, aqui está o exemplo que eu vim com. A preocupação em qualquer monkeying com a cadeia pedido é que precisamos ter certeza de que o manipulador é seguro para várias solicitações, que é por isso que estou desconfortável apenas substituindo a definição de putheader na classe HttpConnection diretamente.

Infelizmente, porque as partes internas do HttpConnection eo AbstractHTTPHandler são muito interna, temos que reproduzir grande parte do código da biblioteca python para injetar nosso comportamento personalizado. Supondo que eu não tenha gozado abaixo e isso funciona bem, como o fez em minha 5 minutos de teste, por favor, tenha cuidado para revisitar essa substituição se você atualizar sua versão Python para um número de revisão (ou seja: 2.5.x para 2.5.y ou 2,5-2,6, etc.).

Eu deveria, portanto, mencionar que eu estou em Python 2.5.1. Se você tiver 2.6 ou, particularmente, 3.0, você pode precisar de ajustar esta em conformidade.

Por favor, deixe-me saber se isso não funcionar. Estou tendo waaaayyyy demasiado divertimento com esta pergunta:

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

Como sobre algo como isto:

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

Uma solução de baixo nível:

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

Exemplo:

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

Uma outra solução, bruxa usou a idéia de Como você recebe cabeçalhos padrão em uma solicitação urllib2 Mas não copiar o 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

EDIT: atualizar a fonte

ver urllib2.py:do_request (linha 1044 (1067)) e urllib2.py:do_open (linha 1073) (Linha 293) self.addheaders = [( 'User-agent', client_version)] (apenas 'Utilizador-agente' adicionado)

Parece-me que você está procurando os cabeçalhos do objeto de resposta, que incluem Connection: close, etc. Estes cabeçalhos vivem no objeto retornado por urlopen. Obtendo para eles é bastante fácil:

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

req.headers é uma instância de httplib.HTTPMessage

Deve enviar o padrão http headers (conforme especificado pelo w3.org ) ao lado os que você especificar. Você pode usar uma ferramenta como WireShark se você gostaria de vê-los em sua totalidade.

Editar:

Se você gostaria de registrá-los, você pode usar WinPcap para capturar pacotes enviados por aplicações específicas ( no seu caso, python). Você também pode especificar o tipo de pacotes e muitos outros detalhes.

-John

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top