2009-03-11 20 views
73

Ich spiele herum und versuche, Code zu schreiben, um die tr.im APIs zu verwenden, um eine URL zu verkürzen.Python urllib2, grundlegende HTTP-Authentifizierung und tr.im

Nach http://docs.python.org/library/urllib2.html lesen, habe ich versucht:

TRIM_API_URL = 'http://api.tr.im/api' 
    auth_handler = urllib2.HTTPBasicAuthHandler() 
    auth_handler.add_password(realm='tr.im', 
          uri=TRIM_API_URL, 
          user=USERNAME, 
          passwd=PASSWORD) 
    opener = urllib2.build_opener(auth_handler) 
    urllib2.install_opener(opener) 
    response = urllib2.urlopen('%s/trim_simple?url=%s' 
           % (TRIM_API_URL, url_to_trim)) 
    url = response.read().strip() 

response.code 200 ist (ich glaube, es 202 sein sollte). URL ist gültig, aber die grundlegende HTTP-Authentifizierung scheint nicht funktioniert haben, weil die gekürzte URL ist nicht in meiner Liste der URLs (um http://tr.im/?page=1).

Nach http://www.voidspace.org.uk/python/articles/authentication.shtml#doing-it-properly Lesen ich auch versucht:

TRIM_API_URL = 'api.tr.im/api' 
    password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() 
    password_mgr.add_password(None, TRIM_API_URL, USERNAME, PASSWORD) 
    auth_handler = urllib2.HTTPBasicAuthHandler(password_mgr) 
    opener = urllib2.build_opener(auth_handler) 
    urllib2.install_opener(opener) 
    response = urllib2.urlopen('http://%s/trim_simple?url=%s' 
           % (TRIM_API_URL, url_to_trim)) 
    url = response.read().strip() 

Aber ich die gleichen Ergebnisse erhalten. (Response.code 200 und URL gültig ist, aber nicht auf http://tr.im/ in meinem Konto aufgezeichnet.)

Wenn ich Abfrage-String-Parameter verwenden, anstatt grundlegende HTTP-Authentifizierung, wie folgt aus:

TRIM_API_URL = 'http://api.tr.im/api' 
    response = urllib2.urlopen('%s/trim_simple?url=%s&username=%s&password=%s' 
           % (TRIM_API_URL, 
           url_to_trim, 
           USERNAME, 
           PASSWORD)) 
    url = response.read().strip() 

. ..dann ist die URL nicht nur gültig, sondern auch in meinem tr.im Account. (Obwohl response.code ist noch 200.)

Es etwas falsch sein muss, wenn auch mit meinem Code (und nicht tr.im API), weil

$ curl -u yacitus:xxxx http://api.tr.im/api/trim_url.json?url=http://www.google.co.uk 

... Rückkehr:

{"trimpath":"hfhb","reference":"nH45bftZDWOX0QpVojeDbOvPDnaRaJ","trimmed":"11\/03\/2009","destination":"http:\/\/www.google.co.uk\/","trim_path":"hfhb","domain":"google.co.uk","url":"http:\/\/tr.im\/hfhb","visits":0,"status":{"result":"OK","code":"200","message":"tr.im URL Added."},"date_time":"2009-03-11T10:15:35-04:00"} 

... und die URL erscheint in meiner Liste der URLs auf http://tr.im/?page=1.

Und wenn ich laufen:

$ curl -u yacitus:xxxx http://api.tr.im/api/trim_url.json?url=http://www.google.co.uk 

... wieder, die ich erhalten:

{"trimpath":"hfhb","reference":"nH45bftZDWOX0QpVojeDbOvPDnaRaJ","trimmed":"11\/03\/2009","destination":"http:\/\/www.google.co.uk\/","trim_path":"hfhb","domain":"google.co.uk","url":"http:\/\/tr.im\/hfhb","visits":0,"status":{"result":"OK","code":"201","message":"tr.im URL Already Created [yacitus]."},"date_time":"2009-03-11T10:15:35-04:00"} 

Hinweis Code ist 201, und die Nachricht ist „tr.im URL bereits erstellt [yacitus]. "

Ich muss nicht die grundlegende HTTP-Authentifizierung korrekt (in beiden Versuchen) tun. Kannst du mein Problem erkennen? Vielleicht sollte ich schauen und sehen, was über die Leitung geschickt wird? Das habe ich noch nie gemacht. Gibt es Python-APIs, die ich verwenden kann (vielleicht in pdb)? Oder gibt es ein anderes Tool (vorzugsweise für Mac OS X), das ich verwenden kann?

+2

Die Website muss "WWW-Authenticate" und Code 401 zurückgeben, bevor urllib2 (oder httplib2) Ihre Anmeldeinformationen sendet. [Siehe meine Antwort unten] (http://stackoverflow.com/questions/635113/python-urllib2-basic-http-authentication-and-tr-im/9698319#9698319). –

+0

Hinweis: Dieser Dienst scheint nicht zu funktionieren. – Laurel

Antwort

229

Dies scheint sehr gut funktionieren (von einem anderen Thread übernommen)

import urllib2, base64 

request = urllib2.Request("http://api.foursquare.com/v1/user") 
base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '') 
request.add_header("Authorization", "Basic %s" % base64string) 
result = urllib2.urlopen(request) 
+3

[ Quelle] (http://StackOverflow.com/a/2955687/708764) – seler

+7

Verwenden Sie anstelle von base64.encodetring und replace base64.standard_b64encode –

+5

'request.add_header ('Autorisierung', b'Basic '+ base64.b64encode (Benutzername + b ':' + Passwort)) ' – jfs

18

Wirklich billig Lösung:

urllib.urlopen('http://user:[email protected]/api') 

(welche Sie sich entscheiden, können für eine Reihe von Gründen nicht geeignet ist, wie die Sicherheit der URL)

Github API example:

>>> import urllib, json 
>>> result = urllib.urlopen('https://personal-access-token:[email protected]/repos/:owner/:repo') 
>>> r = json.load(result.fp) 
>>> result.close() 
+0

Gibt es Vorteile gegenüber dem Verwenden von Abfragezeichenfolgenparametern? –

+0

Daryl: Wenn es funktioniert, würde ich sagen, dass es ein Vorteil ist, ja, und wahrscheinlich sicherer als Abfrage String Argumente, da die meisten HTTP-Clients ein bisschen vorsichtiger sind, wie sie damit umgehen. –

+0

Ich werde wahrscheinlich damit (so dass Sie meinen upvote) gehen, aber ich möchte immer noch herausfinden, was mit meinem Code falsch ist (so wird dies nicht meine akzeptierte Antwort sein). –

12

Werfen Sie einen Blick auf this SO post answer und schauen Sie sich auch diese basic authentication tutorial aus der urllib2 missing manual.

Damit urllib2 Standardauthentifizierung zu arbeiten, die HTTP-Antwort HTTP-Code 401 Unauthorized enthalten und einen Schlüssel "WWW-Authenticate" mit dem Wert "Basic" sonst, Python wird Ihre Login-Infos nicht senden, und Sie müssen Verwenden Sie entweder Requests oder urllib.urlopen(url) mit Ihrem Login in der URL, oder fügen Sie einen Header wie in @Flowpoke'sanswer hinzu.

können Sie Ihre Fehler anzeigen, indem Sie Ihre urlopen in einem try-Block setzen:

try: 
    urllib2.urlopen(urllib2.Request(url)) 
except urllib2.HTTPError, e: 
    print e.headers 
    print e.headers.has_key('WWW-Authenticate') 
+0

Das hat mir geholfen, weil mir das Drucken der Header zu der Erkenntnis führte, dass ich den Authentifizierungsbereich typisiert hatte. +1 – freespace

2

Gleiche Lösungen wie Python urllib2 Basic Auth Problem gelten.

siehe https://stackoverflow.com/a/24048852/1733117; Sie können die Unterklasse urllib2.HTTPBasicAuthHandler hinzufügen, um den Header Authorization zu jeder Anfrage hinzuzufügen, die der bekannten URL entspricht.

class PreemptiveBasicAuthHandler(urllib2.HTTPBasicAuthHandler): 
    '''Preemptive basic auth. 

    Instead of waiting for a 403 to then retry with the credentials, 
    send the credentials if the url is handled by the password manager. 
    Note: please use realm=None when calling add_password.''' 
    def http_request(self, req): 
     url = req.get_full_url() 
     realm = None 
     # this is very similar to the code from retry_http_basic_auth() 
     # but returns a request object. 
     user, pw = self.passwd.find_user_password(realm, url) 
     if pw: 
      raw = "%s:%s" % (user, pw) 
      auth = 'Basic %s' % base64.b64encode(raw).strip() 
      req.add_unredirected_header(self.auth_header, auth) 
     return req 

    https_request = http_request 
+0

Ist der Aufruf von 'strip' nach' b64encode' nicht redundant? –

2

Ich würde vorschlagen, dass die aktuelle Lösung ist mein Paket verwenden urllib2_prior_auth, die diese ziemlich gut löst (I auf inclusion dem Standard lib arbeiten

6

The recommended way zu verwenden ist requests module.

#!/usr/bin/env python 
import requests # $ python -m pip install requests 
####from pip._vendor import requests # bundled with python 

url = 'https://httpbin.org/hidden-basic-auth/user/passwd' 
user, password = 'user', 'passwd' 

r = requests.get(url, auth=(user, password)) # send auth unconditionally 
r.raise_for_status() # raise an exception if the authentication fails 

Hier ist eine einzige Quelle Python 2/3 kompatibel urllib2 -basierte Variante:

#!/usr/bin/env python 
import base64 
try: 
    from urllib.request import Request, urlopen 
except ImportError: # Python 2 
    from urllib2 import Request, urlopen 

credentials = '{user}:{password}'.format(**vars()).encode() 
urlopen(Request(url, headers={'Authorization': # send auth unconditionally 
    b'Basic ' + base64.b64encode(credentials)})).close() 

Python 3.5+ introduces HTTPPasswordMgrWithPriorAuth(), die erlaubt:

..um unnötiges 401 Response-Handling zu beseitigen oder zu bedingungslos Anmeldeinformationen auf der ersten Anforderung, um mit Servern, die eine 404-Antwort anstelle ein 401, wenn die Berechtigung zurückgeben zu kommunizieren schicken falls erforderlich in diesem Fall Header wird nicht ..

#!/usr/bin/env python3 
import urllib.request as urllib2 

password_manager = urllib2.HTTPPasswordMgrWithPriorAuth() 
password_manager.add_password(None, url, user, password, 
           is_authenticated=True) # to handle 404 variant 
auth_manager = urllib2.HTTPBasicAuthHandler(password_manager) 
opener = urllib2.build_opener(auth_manager) 

opener.open(url).close() 

Es ist leicht HTTPBasicAuthHandler() zu ersetzen mit ProxyBasicAuthHandler() gesendet.