2009-03-11 22 views
73

Gram w kółko, próbuję napisać kod, aby użyć interfejsów API tr.im , aby skrócić adres URL.Python urllib2, podstawowe uwierzytelnianie HTTP i tr.im

Po przeczytaniu http://docs.python.org/library/urllib2.html, próbowałem:

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 (myślę, że powinno być 202). adres URL jest prawidłowy, ale podstawowe uwierzytelnienie HTTP nie działa, ponieważ skrócony adres URL nie znajduje się na mojej liście adresów URL (pod adresem http://tr.im/?page=1).

Po przeczytaniu http://www.voidspace.org.uk/python/articles/authentication.shtml#doing-it-properly Próbowałem również:

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

Ale mam takie same wyniki. (Response.code 200 i adres URL jest poprawny, ale nie rejestrowane na moim rachunku w http://tr.im/.)

Jeśli używam kwerendy parametry ciągu zamiast podstawowego uwierzytelniania HTTP tak:

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

. ..wtedy nie tylko adres URL jest prawidłowy, ale jest zapisany na moim koncie tr.im. (Choć response.code wciąż 200.)

Tam musi być coś nie tak z moim kodem chociaż (a nie tr.im API), ponieważ

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

... powroty:

{"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"} 

... a adres URL pojawi się na mojej liście adresów URL pod numerem http://tr.im/?page=1.

A jeśli biegnę:

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

... ponownie, otrzymuję:

{"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"} 

kod Note jest 201, a komunikat jest „tr.im URL już stworzone [yacitus]. "

Nie mogę poprawnie wykonywać podstawowego uwierzytelniania HTTP (w obu przypadkach). Czy możesz zauważyć mój problem? Może powinienem zobaczyć i zobaczyć, co jest wysyłane przez przewód? Nigdy wcześniej tego nie robiłem. Czy mogę używać API Pythona (być może w pdb)? Czy istnieje inne narzędzie (najlepiej na Mac OS X), którego mogę użyć?

+2

Witryna musi zwrócić '' WWW-Authenticate "' i kod 401 zanim urllib2 (lub httplib2) wyśle ​​twoje referencje. [Zobacz moją odpowiedź poniżej] (http://stackoverflow.com/questions/635113/python-urllib2-basic-http-authentication-and-tr-im/9698319#9698319). –

+0

Uwaga: ta usługa wydaje się być nieistniejąca. – Laurel

Odpowiedz

229

Wydaje się to pracować naprawdę dobrze (wzięte z innego wątku)

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

[ source] (http://stackoverflow.com/a/2955687/708764) – seler

+7

Zamiast base64.encodestring i replace, użyj base64.standard_b64encode –

+5

'request.add_header ('Authorization', b'Basic '+ base64.b64encode (nazwa użytkownika + b ':' + hasło)) ' – jfs

18

naprawdę tanie rozwiązanie:

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

(który może zdecydować, nie jest odpowiedni dla wielu powodów, takich jak bezpieczeństwo 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

Czy są jakieś zalety związane z używaniem parametrów ciągu zapytania? –

+0

Daryl: jeśli to działa, powiedziałbym, że jest to zaleta tak, i prawdopodobnie bardziej bezpieczne niż argumenty ciągu zapytania, ponieważ większość klientów http jest trochę bardziej ostrożna, jak sobie z nimi radzą. –

+0

Prawdopodobnie pójdę z tym (więc dostaniesz moją wiadomość), ale nadal chciałbym dowiedzieć się, co jest nie tak z moim kodem (więc to nie będzie moja akceptowana odpowiedź). –

12

Spójrz na this SO post answer, a także spójrz na to basic authentication tutorial z urllib2 missing manual.

Aby urllib2 podstawowe uwierzytelnianie do pracy, odpowiedź http musi zawierać kod HTTP 401 Nieautoryzowany i kluczową "WWW-Authenticate" z wartością "Basic" inaczej, Python nie wyśle ​​swoje dane logowania i trzeba będzie użyć Requests lub urllib.urlopen(url) przy próbie zalogowania się w url lub dodać nagłówek jak w @Flowpoke'sanswer.

Można zobaczyć swój błąd poprzez umieszczenie swojej urlopen w bloku try:

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

Pomogło mi to, ponieważ drukowanie nagłówków doprowadziło mnie do przekonania, że ​​wpisałem sferę uwierzytelniania. +1 – freespace

2

same rozwiązania jak Python urllib2 Basic Auth Problem zastosowania.

zobaczyć https://stackoverflow.com/a/24048852/1733117; możesz podklasę urllib2.HTTPBasicAuthHandler dodać nagłówek Authorization do każdego żądania pasującego do znanego adresu URL.

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

Czy wywołanie 'strip' nie jest nadmiarowe po' b64encode'? –

2

Sugerowałbym, że obecne rozwiązanie jest do korzystania z mojego pakietu urllib2_prior_auth który rozwiązuje ten całkiem ładnie (pracuję na inclusion do standardowego lib

6

The recommended way jest użycie 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 

Oto jedno źródło zgodne z Pythonem 2/3 urllib2 -podstawiona wersja:

#!/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() który umożliwia:

..to wyeliminować niepotrzebną 401 prowadzenie reakcji lub wysłać poświadczenia bezwarunkowo na pierwsze żądanie w celu komunikowania się z serwerami, które zwracają się odpowiedź 404 zamiast 401, jeżeli zezwolenie nagłówek nie jest wysyłany ..

#!/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() 

łatwo wymienić HTTPBasicAuthHandler() z ProxyBasicAuthHandler() jeśli w tym przypadku konieczne.

Powiązane problemy