Python urllib2, authentification HTTP de base, et tr.im

je suis en train de jouer, essayant d'écrire un code pour utiliser le tr.im APIs pour raccourcir une URL.

après http://docs.python.org/library/urllib2.html , j'ai essayé:

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

réponse.code 200 (je pense qu'il devrait être 202). l'url est valide, mais L'authentification HTTP de base ne semble pas avoir fonctionné, car URL raccourci n'est pas dans ma liste D'URLs (à http://tr.im/?page=1 ).

après http://www.voidspace.org.uk/python/articles/authentication.shtml#doing-it-properly J'ai aussi essayé:

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

Mais j'obtiens les mêmes résultats. (réponse.le code est 200 et l'url est valide, mais pas enregistré dans mon compte à http://tr.im / .)

si j'utilise les paramètres de la chaîne de requête au lieu de l'authentification HTTP de base, comme ceci:

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

...alors non seulement l'url est valide, mais elle est enregistrée dans mon tr.compte im. (Si réponse.le code est toujours à 200.)

il doit y avoir quelque chose qui ne va pas avec mon code (et non tr.IM'S API), parce que

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

...retourne:

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

...et L'URL apparaît dans ma liste D'URLs sur http://tr.im/?page=1 .

et si exécuter:

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

...encore une fois, je reçois:

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

Note code est 201, et le message est "tr.im URL Déjà Créé [yacitus]."

Je ne dois pas faire l'authentification HTTP de base correctement (dans les deux cas). Tu peux repérer mon problème? Peut-être devrais-je regarder ce qui est envoyé par télégramme? Je n'ai jamais fait ça avant. Y a-t-il des APIs Python que je peux utiliser (peut-être dans pdb)? Ou est-il un autre outil (de préférence pour Mac OS X) je peux utiliser?

78
demandé sur Laurel 2009-03-11 18:43:10

7 réponses

Cela semble fonctionner vraiment bien (prises à partir d'un autre thread)

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
répondu Flowpoke 2010-11-15 20:52:54

Vraiment pas cher solution:

urllib.urlopen('http://user:xxxx@api.tr.im/api')

(que vous pouvez décider n'est pas adapté pour un certain nombre de raisons, comme la sécurité de l'url)

exemple D'API GitHub :

>>> import urllib, json
>>> result = urllib.urlopen('https://personal-access-token:x-oauth-basic@api.github.com/repos/:owner/:repo')
>>> r = json.load(result.fp)
>>> result.close()
19
répondu Ali Afshar 2014-09-05 09:04:54

jetez un oeil à cette réponse ainsi post et aussi regarder ce tutoriel d'authentification de base de la urllib2 manuel manquant .

pour que l'authentification de base urllib2 fonctionne, la réponse http doit contenir le code HTTP 401 non autorisé et une clé "WWW-Authenticate" avec la valeur "Basic" sinon, Python n'enverra pas votre login info, et vous devrez utiliser Requests , ou urllib.urlopen(url) avec votre login dans l'url, ou ajouter un en-tête comme dans @Flowpoke réponse .

vous pouvez visualiser votre erreur en mettant votre urlopen dans un bloc d'essai:

try:
    urllib2.urlopen(urllib2.Request(url))
except urllib2.HTTPError, e:
    print e.headers
    print e.headers.has_key('WWW-Authenticate')
12
répondu Mark Mikofski 2017-05-23 12:26:00

la voie recommandée est d'utiliser 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

Voici une source unique compatible Python 2/3 urllib2 variante basée sur:

#!/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+ introduit HTTPPasswordMgrWithPriorAuth() qui permet:

.. pour éliminer le traitement inutile des réponses 401, ou pour envoyer inconditionnellement des justificatifs d'identité sur la première requête pour communiquer avec des serveurs qui renvoient une réponse 404 au lieu d'une réponse 401 si l'en-tête D'autorisation n'est pas envoyé..

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

il est facile de remplacer HTTPBasicAuthHandler() par ProxyBasicAuthHandler() si nécessaire dans ce cas.

6
répondu jfs 2015-11-03 21:39:08

mêmes solutions que Python urllib2 Basic Auth Problem apply.

voir https://stackoverflow.com/a/24048852/1733117 ; vous pouvez sous-classe urllib2.HTTPBasicAuthHandler pour ajouter l'en-tête Authorization à chaque requête qui correspond à l'url connue.

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
répondu dnozay 2017-05-23 12:10:04

je suggère que la solution actuelle est d'utiliser mon paquet urllib2_prior_auth ce qui résout assez bien ce problème (je travaille sur inclusion à la norme lib.

2
répondu mcepl 2014-09-10 21:38:56
1
répondu Andrew G 2012-11-11 02:44:58