HTTPPasswordMgr urllib2 no funciona – Error de credenciales no enviadas

La siguiente llamada de Python Curl tiene los siguientes resultados exitosos:

>>> import subprocess >>> args = [ 'curl', '-H', 'X-Requested-With: Demo', 'https://username:password@qualysapi.qualys.com/qps/rest/3.0/count/was/webapp' ] >>> xml_output = subprocess.check_output(args).decode('utf-8') % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 138 276 0 276 0 0 190 0 --:--:-- 0:00:01 --:--:-- 315 >>> xml_output u'\n\nSUCCESS\n 33\n' 

Desafortunadamente, esta llamada no se traduce exitosamente a urllib2. Recibo una respuesta XML diferente que indica que el usuario no proporcionó las credenciales de autorización:

 >>> import urllib2 >>> # Create a password manager. ... password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() >>> # Add the username and password. ... top_level_url = 'https://qualysapi.qualys.com' >>> password_mgr.add_password(None, top_level_url, username, password) >>> handler = urllib2.HTTPBasicAuthHandler(password_mgr) >>> opener = urllib2.build_opener(handler) >>> urllib2.install_opener(opener) >>> headers = {'X-Requested-With':'Demo'} >>> uri = 'https://qualysapi.qualys.com/qps/rest/3.0/count/was/webapp' >>> req = urllib2.Request(uri,None,headers) >>> result = urllib2.urlopen(req) >>> result '\n INVALID_CREDENTIALS\n \n User did not supply any authentication headers\n \n' 

Por cierto, recibo el mismo mensaje de error con httplib:

 >>> import httplib, base64 >>> auth = 'Basic ' + string.strip(base64.encodestring(username + ':' + password)) >>> h = httplib.HTTPSConnection('qualysapi.qualys.com') >>> h.request("GET", "/qps/rest/3.0/count/was/webapp/") >>> r1 = h.getresponse() >>> print r1.status, r1.reason 200 OK >>> data1 = r1.read() >>> data1 '\n INVALID_CREDENTIALS\n \n User did not supply any authentication headers\n \n' 

Entiendo que httplib & urllib2 solo puede funcionar si SSL se comstack en socket, que SSL se comstack en el módulo de socket. De hecho, he usado urllib2 con éxito para otras llamadas en una API diferente. El problema está aislado a esta API específica.

¿Qué hace urllib2 (y httplib) de forma diferente a curl?

Nota: el nombre de usuario y la contraseña utilizados son los mismos en todos los ejemplos.

Actualizar:

El problema es con el administrador de contraseña de autenticación básica. Cuando agrego manualmente el encabezado de autorización básico, urllib2 cal funciona:

 >>> import base64 >>> base64string = base64.encodestring('%s:%s' % (username, password))[:-1] >>> req.add_header("Authorization", "Basic %s" % base64string) >>> # Make request to fetch url. ... result = urllib2.urlopen(req) >>> # Read xml results. ... xml = result.read() >>> xml '\n\n SUCCESS\n 33\n' 

Desde Python urllib2 Basic Auth Problem

El problema [es] que las bibliotecas de Python, por HTTP-Standard, primero envían una solicitud no autenticada, y luego solo si se responde con un rebash 401, se envían las credenciales correctas. Si los … servidores no realizan una “autenticación totalmente estándar”, las bibliotecas no funcionarán.

Esta API en particular no responde con un 401 no autorizado en el primer bash, responde con una respuesta XML que contiene el mensaje de que las credenciales no se enviaron con un código de respuesta 200 OK.

Intente configurar el agente de usuario, tal vez eso es lo que está interfiriendo. urllib2 se identifica a sí mismo como Python-urllib/xy (donde xey son los números de versión mayor y menor de la versión de Python, por ejemplo, Python-urllib/2.5 ) esto podría ser lo que hace que el sitio bloquee su solicitud. Eche un vistazo a su archivo robots.txt … aquí hay un ejemplo sobre cómo configurar el agente de usuario para que su script se identifique como un navegador:

 import urllib import urllib2 url = 'http://www.someserver.com/cgi-bin/register.cgi' user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' headers = { 'User-Agent' : user_agent } req = urllib2.Request(url, data, headers) response = urllib2.urlopen(req) the_page = response.read()