Question

J'ai un client Web Python qui utilise urllib2. Il est assez facile d'ajouter des en-têtes HTTP à mes requêtes sortantes. Je viens de créer un dictionnaire des en-têtes que je veux ajouter et je le passe à l'initialiseur de demande.

Cependant, d’autres "standards" " Les en-têtes HTTP sont ajoutés à la demande ainsi que ceux personnalisés que j’ai explicitement ajoutés. Lorsque je renifle la demande à l'aide de Wireshark, je vois des en-têtes en plus de ceux que j'ai ajoutés moi-même. Ma question est la suivante: comment puis-je accéder à ces en-têtes? Je souhaite consigner toutes les demandes (y compris l'ensemble d'en-têtes HTTP complets ), mais je ne vois pas comment.

des pointeurs?

En résumé: comment puis-je obtenir tous les en-têtes sortants d’une requête HTTP créée par urllib2?

Était-ce utile?

La solution

Si vous voulez voir la requête HTTP littérale qui est envoyée, et donc voir chaque dernier en-tête exactement tel qu'il est représenté sur le câble, vous pouvez indiquer à urllib2 d'utiliser votre propre version de un HTTPHandler qui imprime (ou enregistre, ou autre) la requête HTTP sortante.

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

Le résultat de l'exécution de ce code est:

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

Autres conseils

La bibliothèque urllib2 utilise des objets OpenerDirector pour gérer l’ouverture réelle. Heureusement, la bibliothèque python fournit des valeurs par défaut afin que vous n'ayez pas à le faire. Cependant, ce sont ces objets OpenerDirector qui ajoutent les en-têtes supplémentaires.

Pour voir ce qu'ils sont après l'envoi de la demande (pour pouvoir la consigner par exemple):

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)

Le unredirected_hdrs est l'endroit où les OpenerDirectors vident leurs en-têtes supplémentaires. Il suffit de regarder req.headers pour afficher uniquement vos propres en-têtes - la bibliothèque vous en laisse les droits.

Si vous avez besoin de voir les en-têtes avant d'envoyer la demande, vous devez sous-classer OpenerDirector afin d'intercepter la transmission.

L’espoir que cela aide.

EDIT: j'ai oublié de mentionner qu'une fois la demande envoyée, req.header_items () vous donnera une liste de tuples de TOUS les en-têtes, avec les vôtres et ceux ajoutés. par le OpenerDirector. J'aurais dû le mentionner en premier car c'est le plus simple :-) Désolé.

EDIT 2: Après votre question sur un exemple de définition de votre propre gestionnaire, voici l’exemple que j’ai proposé. La préoccupation de tout singe avec la chaîne de demandes est que nous devons nous assurer que le gestionnaire est sûr pour plusieurs demandes, c'est pourquoi je ne suis pas à l'aise de simplement remplacer la définition de putheader sur la classe HTTPConnection.

Malheureusement, les composants internes de HTTPConnection et de AbstractHTTPHandler étant très internes, nous devons reproduire une grande partie du code de la bibliothèque python pour injecter notre comportement personnalisé. En supposant que je n'ai pas fait de gaffe ci-dessous et que cela fonctionne aussi bien que dans mes 5 minutes de test, veillez à revoir cette substitution si vous mettez à jour votre version de Python avec un numéro de révision (c'est-à-dire: 2.5.x à 2.5.y ou 2,5 à 2,6, etc.).

Je dois donc mentionner que je suis sur Python 2.5.1. Si vous avez 2.6 ou, en particulier, 3.0, vous devrez peut-être ajuster cela en conséquence.

S'il vous plaît laissez-moi savoir si cela ne fonctionne pas. Je me fais trop plaisir avec cette question:

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

Que diriez-vous de quelque chose comme ça:

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

Une solution de bas niveau:

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

Exemple:

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

Une autre solution, qui utilise l'idée de Comment obtenir les en-têtes par défaut dans une requête urllib2? Mais ne copie pas le code à partir 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: met à jour la source

voir urllib2.py:do_request (ligne 1044 (1067)) et urllib2.py:do_open (ligne 1073) (ligne 293) self.addheaders = [('User-agent', version_client)] (uniquement 'User-agent' ajouté)

Il me semble que vous cherchez les en-têtes de l'objet de réponse, qui incluent Connexion: fermer , etc. Ces en-têtes résident dans l'objet renvoyé par urlopen. Les atteindre est assez facile:

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

req.headers est une instance de httplib.HTTPMessage

Il devrait envoyer les en-têtes http par défaut (comme spécifié par w3.org ) à côté de ceux que vous spécifiez. Vous pouvez utiliser un outil tel que WireShark pour les afficher intégralement.

Modifier:

Si vous souhaitez les consigner, vous pouvez utiliser WinPcap pour capturer les paquets envoyés par des applications spécifiques ( dans votre cas, python). Vous pouvez également spécifier le type de paquets et de nombreux autres détails.

-John

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top