Python urllib2基本认证问题

更新: 基于 Lee 的评论,我决定将我的代码压缩成一个非常简单的脚本,并从命令行运行它:

import urllib2
import sys


username = sys.argv[1]
password = sys.argv[2]
url = sys.argv[3]
print("calling %s with %s:%s\n" % (url, username, password))


passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
passman.add_password(None, url, username, password)
urllib2.install_opener(urllib2.build_opener(urllib2.HTTPBasicAuthHandler(passman)))


req = urllib2.Request(url)
f = urllib2.urlopen(req)
data = f.read()
print(data)

不幸的是,它仍然不会生成 Authorization头文件(每个 Wireshark) : (

我在通过 urllib2发送基本 AUTH 时遇到了问题。我看了一下 这篇文章,并遵循了这个例子。我的代码:

passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
passman.add_password(None, "api.foursquare.com", username, password)
urllib2.install_opener(urllib2.build_opener(urllib2.HTTPBasicAuthHandler(passman)))


req = urllib2.Request("http://api.foursquare.com/v1/user")
f = urllib2.urlopen(req)
data = f.read()

我通过 wireshark 在《火线》上看到了以下内容:

GET /v1/user HTTP/1.1
Host: api.foursquare.com
Connection: close
Accept-Encoding: gzip
User-Agent: Python-urllib/2.5

与通过 curl: curl -u user:password http://api.foursquare.com/v1/user发送请求相比,您可以看到授权未被发送

GET /v1/user HTTP/1.1
Authorization: Basic =SNIP=
User-Agent: curl/7.19.4 (universal-apple-darwin10.0) libcurl/7.19.4 OpenSSL/0.9.8k zlib/1.2.3
Host: api.foursquare.com
Accept: */*

出于某种原因,我的代码似乎没有发送身份验证-有人看到我错过了什么吗?

谢谢

Simon

122570 次浏览

第二个参数必须是 URI,而不是域名。

passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
passman.add_password(None, "http://api.foursquare.com/", username, password)

问题可能是,按照 HTTP-Standard,Python 库首先发送一个未经身份验证的请求,然后只有在用401重试应答该请求时,才发送正确的凭据。如果 Foursquare 服务器不进行“完全标准的身份验证”,那么这些库就无法工作。

尝试使用 Header 进行身份验证:

import urllib2, base64


request = urllib2.Request("http://api.foursquare.com/v1/user")
base64string = base64.b64encode('%s:%s' % (username, password))
request.add_header("Authorization", "Basic %s" % base64string)
result = urllib2.urlopen(request)

遇到了与您相同的问题,并从这个线程中找到了解决方案: http://forums.shopify.com/categories/9/posts/27662

(复制/粘贴/改编自 https://stackoverflow.com/a/24048772/1733117)。

首先,您可以继承 urllib2.BaseHandlerurllib2.HTTPBasicAuthHandler类,并实现 http_request,以便每个请求都有适当的 Authorization头。

import urllib2
import base64


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

然后,如果你像我一样懒惰,安装全局处理程序

api_url = "http://api.foursquare.com/"
api_username = "johndoe"
api_password = "some-cryptic-value"


auth_handler = PreemptiveBasicAuthHandler()
auth_handler.add_password(
realm=None, # default realm.
uri=api_url,
user=api_username,
passwd=api_password)
opener = urllib2.build_opener(auth_handler)
urllib2.install_opener(opener)

我建议当前的解决方案是使用我的包 Urllib2 _ prior _ auth,它可以很好地解决这个问题(我将 包容转换为标准库)。

下面是我在尝试访问 MailChimp 的 API 时遇到的类似问题的处理方法。这个也是一样的,只是格式更好一些。

import urllib2
import base64


chimpConfig = {
"headers" : {
"Content-Type": "application/json",
"Authorization": "Basic " + base64.encodestring("hayden:MYSECRETAPIKEY").replace('\n', '')
},
"url": 'https://us12.api.mailchimp.com/3.0/'}


#perform authentication
datas = None
request = urllib2.Request(chimpConfig["url"], datas, chimpConfig["headers"])
result = urllib2.urlopen(request)