Estoy jugando, tratando de escribir código para usar las API de tr.im para acortar una URL.
Después de leer http://docs.python.org/library/urllib2.html , probé:
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()
El código de respuesta es 200 (creo que debería ser 202). url es válida, pero la autenticación HTTP básica no parece haber funcionado, porque la URL abreviada no está en mi lista de URL (en http://tr.im/?page=1 ).
Después de leer http://www.voidspace.org.uk/python/articles/authentication.shtml#doing-it-properly también probé:
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()
Pero obtengo los mismos resultados. (El código de respuesta es 200 y la URL es válida, pero no está registrada en mi cuenta en http://tr.im/ ).
Si uso parámetros de cadena de consulta en lugar de la autenticación HTTP básica, así:
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()
... entonces no solo la URL es válida, sino que está registrada en mi cuenta tr.im. (Aunque el código de respuesta sigue siendo 200).
Sin embargo, debe haber algo mal con mi código (y no con la API de tr.im), porque
$ curl -u yacitus:xxxx http://api.tr.im/api/trim_url.json?url=http://www.google.co.uk
...devoluciones:
{"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"}
... y la URL aparece en mi lista de URL en http://tr.im/?page=1 .
Y si corro:
$ curl -u yacitus:xxxx http://api.tr.im/api/trim_url.json?url=http://www.google.co.uk
... de nuevo, obtengo:
{"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"}
Tenga en cuenta que el código es 201 y el mensaje es "URL de tr.im ya creada [yacitus]".
No debo realizar correctamente la autenticación HTTP básica (en ninguno de los intentos). ¿Puedes detectar mi problema? ¿Quizás debería mirar y ver qué se envía por cable? Nunca había hecho eso antes. ¿Hay API de Python que pueda usar (quizás en pdb)? ¿O hay otra herramienta (preferiblemente para Mac OS X) que pueda usar?
fuente
"WWW-Authenticate"
y el código 401 antes de que urllib2 (o httplib2) envíe sus credenciales. Vea mi respuesta a continuación .Respuestas:
Esto parece funcionar muy bien (tomado de otro hilo)
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)
fuente
request.add_header('Authorization', b'Basic ' + base64.b64encode(username + b':' + password))
Solución realmente barata:
urllib.urlopen('http://user:[email protected]/api')
(que puede decidir que no es adecuado por varias razones, como la seguridad de la URL)
Ejemplo de API de Github :
>>> import urllib, json >>> result = urllib.urlopen('https://personal-access-token:[email protected]/repos/:owner/:repo') >>> r = json.load(result.fp) >>> result.close()
fuente
Eche un vistazo a esta respuesta de la publicación SO y también a este tutorial de autenticación básico del manual faltante de urllib2 .
Para que funcione la autenticación básica de urllib2, la respuesta http debe contener el código HTTP 401 No autorizado y una clave
"WWW-Authenticate"
con el valor; de lo"Basic"
contrario, Python no enviará su información de inicio de sesión y deberá utilizar Solicitudes ourllib.urlopen(url)
con su inicio de sesión en el url, o agregue un encabezado como en la respuesta de @ Flowpoke .Puede ver su error colocando su
urlopen
en un bloque de prueba:try: urllib2.urlopen(urllib2.Request(url)) except urllib2.HTTPError, e: print e.headers print e.headers.has_key('WWW-Authenticate')
fuente
La forma recomendada es utilizar el
requests
módulo :#!/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
Aquí hay una
urllib2
variante basada en una sola fuente compatible con Python 2/3 :#!/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+ introduce
HTTPPasswordMgrWithPriorAuth()
que permite:#!/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 fácil de reemplazar
HTTPBasicAuthHandler()
conProxyBasicAuthHandler()
si es necesario en este caso.fuente
Sugeriría que la solución actual es usar mi paquete urllib2_prior_auth que resuelve esto bastante bien (trabajo en la inclusión en el estándar lib.
fuente
urrlib.request.HTTPBasicPriorAuthHandler
Se aplican las mismas soluciones que el problema de autenticación básica urllib2 de Python .
ver https://stackoverflow.com/a/24048852/1733117 ; puede crear
urllib2.HTTPBasicAuthHandler
una subclase para agregar elAuthorization
encabezado a cada solicitud que coincida con la URL conocida.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
fuente
strip
redundante la llamada despuésb64encode
?Prueba python-request o python-grab
fuente