Python的urllib2基本身份validation问题

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

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在Wire上看到以下内容:

 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: */* 

由于某种原因,我的代码似乎不发送身份validation – 任何人看到我失踪?

谢谢

-simon

问题可能在于,根据HTTP标准,Python库首先发送一个未经身份validation的请求,然后只有在401重试的情况下进行应答时才会发送正确的凭证。 如果Foursquare服务器不执行“完全标准authentication”,那么这些库将不起作用。

尝试使用头进行身份validation:

 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) 

有同样的问题,并从这个线程find解决scheme: http : //forums.shopify.com/categories/9/posts/27662

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

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

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

首先,您可以http_requesthttp_request ,并实现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) 

以下是我用来处理在尝试访问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) 

我build议目前的解决scheme是使用我的包urllib2_prior_auth ,这很好地解决了这个问题(我的工作包括到标准库。