Python urllib2, podstawowe uwierzytelnianie HTTP oraz tr.im

Bawię się, próbując napisać jakiś kod, aby użyć tr.im API do skracania adresu URL.

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

Odpowiedź.kod to 200 (myślę, że powinno być 202). url jest poprawny, ale podstawowe uwierzytelnianie HTTP wydaje się nie działać, ponieważ skrócony adres URL nie znajduje się na mojej liście adresów URL (at http://tr.im/?page=1).

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

   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. (odpowiedź.kod to 200 a url jest poprawny, ale nie zapisane na moim koncie w http://tr.im/.)

Jeśli używam parametrów ciągu zapytania 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 URL jest poprawny, ale jest zapisany w moim tr.im konto. (Choć odpowiedź.kod to nadal 200.)

Musi być coś nie tak z moim kodem chociaż (i nie tr.im ' s API), ponieważ

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

...zwraca:

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

...i adres URL pojawia się na mojej liście adresów URL na http://tr.im/?page=1 .

A jeśli ucieknę:

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

...znowu dostaję:

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

Uwaga kod to 201, A wiadomość to "tr.im Url już utworzony [yacitus]."

Nie mogę poprawnie wykonywać podstawowego uwierzytelniania HTTP(w obu próbach). Widzisz mój problem? Może powinienem zajrzeć i widzisz, co jest wysyłane przez podsłuch? Nigdy wcześniej tego nie robiłam. Czy są API Pythona, których mogę używać (może w pdb)? A może jest inne narzędzie (najlepiej dla Mac OS X), którego mogę użyć?

Author: Laurel, 2009-03-11

7 answers

To wydaje się działać naprawdę dobrze (zaczerpnię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)
 235
Author: Flowpoke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-15 20:52:54

Naprawdę tanie rozwiązanie:

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

(które mogą Państwo uznać za nieodpowiednie z wielu powodów, takich jak bezpieczeństwo adresu url)

Przykład API Github :

>>> import urllib, json
>>> result = urllib.urlopen('https://personal-access-token:[email protected]/repos/:owner/:repo')
>>> r = json.load(result.fp)
>>> result.close()
 19
Author: Ali Afshar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-05 09:04:54

Spójrz na to więc napisz odpowiedź , a także spójrz na ten podstawowy samouczek uwierzytelniania z urllib2 missing manual.

Aby uwierzytelnianie podstawowe urllib2 działało, odpowiedź http musi zawierać kod HTTP 401 Nieautoryzowany oraz Klucz {[1] } z wartością "Basic" w przeciwnym razie Python nie wyśle Twoich danych logowania i będziesz musiał użyć Requests lub urllib.urlopen(url) z Twoim loginem w adresie url, lub dodać nagłówek jak w @ Flowpoke ' s ODPOWIEDŹ .

Możesz zobaczyć swój błąd, umieszczając urlopen w bloku try:

try:
    urllib2.urlopen(urllib2.Request(url))
except urllib2.HTTPError, e:
    print e.headers
    print e.headers.has_key('WWW-Authenticate')
 12
Author: Mark Mikofski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:00

Zalecanym sposobem jest użycie requests moduł :

#!/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 Pythona 2/3 kompatybilne urllib2 oparte na wariancie:

#!/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 + wprowadza HTTPPasswordMgrWithPriorAuth() to pozwala:

.. aby wyeliminować niepotrzebną obsługę odpowiedzi 401 lub bezwarunkowo wysłać poświadczenia na pierwsze żądanie w celu komunikacji z serwerami, które zwracają odpowiedź 404 zamiast 401, jeśli nagłówek autoryzacji nie jest wysłane..

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

W tym przypadku łatwo jest zastąpić HTTPBasicAuthHandler() ProxyBasicAuthHandler().

 6
Author: jfs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-03 21:39:08

Stosuje się te same rozwiązania co Python urllib2 Basic auth Problem .

Zobacz https://stackoverflow.com/a/24048852/1733117 ; możesz podklasować urllib2.HTTPBasicAuthHandler, aby dodać nagłówek Authorization do każdego żądania, które pasuje 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
 3
Author: dnozay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:04

Sugerowałbym, aby obecnym rozwiązaniem było użycie mojego pakietu urllib2_prior_auth , który rozwiązuje to całkiem ładnie (pracuję nad włączeniem do standardowego lib.

 2
Author: mcepl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-10 21:38:56
 1
Author: Andrew G,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-11 02:44:58