urllib2 und *request* header bekommen...

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ich würde gern die response und request Header eines http request bekommen.

Die response header sind nicht das Problem, die bekommt man einfach mit .info()
Aber die request header oder den raw request content, bekommt man nicht so einfach.

Lösungen werden hier vorgeschlagen:
http://stackoverflow.com/questions/6085 ... tp-request
http://stackoverflow.com/questions/6038 ... b2-request

Aber so richtig brauchbar ist IMHO keine davon :(

Das was ich möchte ist das von http://stackoverflow.com/questions/6038 ... 30#4034430 :

Code: Alles auswählen

import httplib, urllib

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

Code: Alles auswählen

GET / HTTP/1.1
Accept-Encoding: identity
Host: www.google.com
Connection: close
User-Agent: Python-urllib/2.6
Aber natürlich nicht per print, sondern irgendwie von "außerhalb" erreichbar.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ich sehe keine Möglichkeit zusätzliche Informationen von HTTPConnection zurück zu geben, außer man macht es so wie hier: http://stackoverflow.com/questions/6038 ... 966#603966 (zweites Bespiel)... Dabei muß man dann aber eine Menge code aus der Lib übernehmen. Das gefällt mir nicht.

Eine Lösung wäre nicht urllib2 zu nehmen, sondern direkt HTTPConnection zu nutzten:

Code: Alles auswählen

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

print "1:", response.status, response.reason

print "\n\nrequest_headers:\n"
print response.request_headers

print "\n\nrequest_header:\n"
print response.request_header
Ausgaben:

Code: Alles auswählen

1: 200 OK


request_headers:

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


request_header:

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


Aber das ist ein wenig mehr low level :(

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab eine Lösung, die urllib2 nutzt, aber keinen code doppelt kopiert, wie in http://stackoverflow.com/questions/6038 ... 966#603966

Code: Alles auswählen

import httplib
import urllib2


class HTTPConnection2(httplib.HTTPConnection):
    def putheader(self, header, value):
        self.request_headers["header_list"].append((header, value))
        httplib.HTTPConnection.putheader(self, header, value)

    def send(self, s):
        self.request_headers["send"] = s
        httplib.HTTPConnection.send(self, s)


class HTTPHandler2(urllib2.HTTPHandler):
    """
    >>> opener = urllib2.build_opener(HTTPHandler2)
    >>> opener.addheaders = [("User-agent", "Python test")]
    >>> response = opener.open('http://www.google.com/')
    
    Get the request headers as a list build with HTTPConnection.putheader():
    >>> response.request_headers
    [('Accept-Encoding', 'identity'), ('Host', 'www.google.de'), ('Connection', 'close'), ('User-Agent', 'Python test')]
    
    >>> response.request_header
    'GET / HTTP/1.1\\r\\nAccept-Encoding: identity\\r\\nHost: www.google.de\\r\\nConnection: close\\r\\nUser-Agent: Python test\\r\\n\\r\\n'
    """
    def http_open(self, req):
        conn_class = HTTPConnection2
        conn_class.request_headers = {
            "header_list": [],
            "send": "",
        }
        response = self.do_open(conn_class, req)
        response.request_headers = conn_class.request_headers["header_list"]
        response.request_header = conn_class.request_headers["send"]
        return response


if __name__ == "__main__":
    import doctest
    print doctest.testmod(
        verbose=True
    )
Ausgabe des DocTests:

Code: Alles auswählen

...
Trying:
    response.request_headers
Expecting:
    [('Accept-Encoding', 'identity'), ('Host', 'www.google.de'), ('Connection', 'close'), ('User-Agent', 'Python test')]
ok
Trying:
    response.request_header
Expecting:
    'GET / HTTP/1.1\r\nAccept-Encoding: identity\r\nHost: www.google.de\r\nConnection: close\r\nUser-Agent: Python test\r\n\r\n'
ok
...

Weiß nur nicht, ob es evtl. conn_class.request_headers = {} nicht Threadsafe ist?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
deets

jens hat geschrieben: Weiß nur nicht, ob es evtl. conn_class.request_headers = {} nicht Threadsafe ist?
Ich weiss es, und es ist es nicht.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Das eigentliche Problem ist IMHO, das man keine Möglichkeit hat an die Instanz von httplib.HTTPConnection() zu kommen, weil sie innerhalb von urllib2.HTTPHandler.do_open() erstellt und wieder verworfen wird.

Doch ich mir was überlegt: Warum nicht an do_open() direkt eine Instanz übergeben, die aber vom Verhalten her wie das Original ist?

Code: Alles auswählen

# coding:utf-8

import httplib
import urllib2


class HTTPConnection2(httplib.HTTPConnection):
    """
    Same as original httplib.HTTPConnection, but saves the request headers.
    """
    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
    """
    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):
    """
    >>> 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


if __name__ == "__main__":
    import doctest
    print doctest.testmod()

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
deets

Also, ich habe sowas meine ich schonmal gemacht:

https://github.com/soundcloud/python-ap ... _init__.py

Ist ein bisschen schwer zu sehen, abr schau mal in Zeile 203ff. Da baue ich ein eigenes Request, und im OAuth-Handler werden auch eigene Header gesetzt (also kann man die denke ich auch lesen)
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hm. Also ich wüßte nicht wie mir das weiterhelfen sollte.
Ich möchte die request header haben, nicht die response! Also sowas wie das hier:

Code: Alles auswählen

GET / HTTP/1.1
Accept-Encoding: identity
Host: www.heise.de
Connection: close
User-Agent: Python test
EDIT: Das ganze ist nun in django-tools gelandet: https://github.com/jedie/django-tools/b ... ls/http.py

Dort gibt es dann noch den zusatz, das man den eigentlich content in unicode erhalten kann:

Code: Alles auswählen

r = HttpRequest("http://www.google.com")
response = r.get_response()
print "Request headers as list:", response.request_headers
print "Raw Request header:", response.request_header
print r.get_unicode()
Zum unicode wandeln, wollte dabei nicht so viel Aufwand treiben:
Dabei wird einmal charset aus den response header genommen, wenn der nicht da ist oder "falsch" aus dem content alle r'<meta.*?charset=["\']*(.+?)["\'>]' probiert. Wenn das auch nicht klappt dann, fallback: unicode(content, encoding, errors="replace")

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
deets

jens hat geschrieben:Hm. Also ich wüßte nicht wie mir das weiterhelfen sollte.
Ich möchte die request header haben, nicht die response!
Das habe ich schon verstanden. Und wenn du dir das anschaust, dann siehst du, dass ich ein explizites Request-Objekt erzeuge, und darin Header manipuliere. Und wenn du dieses Objekt hast, und damit das Request durchfuehrst, dann kannst du doch danach einfach auf alles, was darin gesetzt wurde, zugreifen.

Da es mit der Hilfe zur Selbsthilfe wohl nicht gereicht hat:

Code: Alles auswählen

import urllib2

req = urllib2.Request("http://www.heise.de/")
opener = urllib2.build_opener()
handle = opener.open(req, None)
info = handle.info()
content = handle.read()
print content, info
print req.__dict__

Kommt bei mir

Code: Alles auswählen

{'_Request__original': 'http://www.heise.de/', 'type': 'http', '_Request__r_type': '//www.heise.de/', 'origin_req_host': 'www.heise.de', 'headers': {}, 'host': 'www.heise.de', '_Request__r_host': '/', 'unredirected_hdrs': {'Host': 'www.heise.de', 'User-agent': 'Python-urllib/2.5'}, 'unverifiable': False, 'data': None, 'port': None}
bei rum. Use-Agent zB ist gesetzt, woher Connection und Accept-encoding bei dir kommen - kA.

Was da passiert, wenn man einen redirect bekommt (dem urllib ja automatisch folgt) weiss ich nicht mehr, aber auch dafuer findet sich in der SoundCloud API der notwendige code - ich musste da wieder header mitgeben, oder so.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ja, ok. Das ist sicherlich schon etwas. Allerdings hat man so nicht die echten gesendeten Header, wie aus meinem vorherigen Post.

EDIT:

Da ist auch was komisch:

Code: Alles auswählen

req = urllib2.Request("http://www.python.org")
opener = urllib2.build_opener()
#opener.addheaders = [("Referer", "/")]
response = opener.open(req, None)

print req.headers
print req.unredirected_hdrs
Liefert:

Code: Alles auswählen

{}
{'Host': 'www.python.org', 'User-agent': 'Python-urllib/2.7'}
Aber:

Code: Alles auswählen

req = urllib2.Request("http://www.python.org")
opener = urllib2.build_opener()
opener.addheaders = [("Referer", "/")]
response = opener.open(req, None)

print req.headers
print req.unredirected_hdrs
Spuckt aus:

Code: Alles auswählen

{}
{'Host': 'www.python.org', 'Referer': '/'}
Wo ist 'User-agent' hin?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Unterstützte nun auch https, in https://github.com/jedie/django-tools/b ... ls/http.py
Aber das hat einiges an doppelten Code zur folge:

Code: Alles auswählen

class HTTPConnection2(httplib.HTTPConnection):
    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)

if hasattr(httplib, 'HTTPS'):
    class HTTPSConnection2(httplib.HTTPSConnection):
        def __init__(self, *args, **kwargs):
            httplib.HTTPSConnection.__init__(self, *args, **kwargs)
            self.request_headers = []
            self.request_header = ""

        def putheader(self, header, value):
            self.request_headers.append((header, value))
            httplib.HTTPSConnection.putheader(self, header, value)

        def send(self, s):
            self.request_header = s
            httplib.HTTPSConnection.send(self, s)
und einmal:

Code: Alles auswählen

class HTTPHandler2(urllib2.HTTPHandler):
    def http_open(self, req):
        conn_instance = HTTPConnectionWrapper(HTTPConnection2)
        response = self.do_open(conn_instance, req)
        response.request_headers = conn_instance.request_headers
        response.request_header = conn_instance.request_header
        return response

if hasattr(httplib, 'HTTPS'):
    class HTTPSHandler2(urllib2.HTTPSHandler):
        def https_open(self, req):
            conn_instance = HTTPConnectionWrapper(HTTPSConnection2)
            response = self.do_open(conn_instance, req)
            response.request_headers = conn_instance.request_headers
            response.request_header = conn_instance.request_header
            return response
Jemand eine Idee, wie man das zusammen fassen könnte?
Leider sind die Original Klassen aus der Lib nur old-style-classes...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten