Python urllib2,基本HTTP身份验证和tr.im.

时间:2009-03-11 15:43:10

标签: python http authentication

我正在玩,尝试编写一些代码来使用tr.im 用于缩短网址的API。

阅读http://docs.python.org/library/urllib2.html后,我尝试了:

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

response.code是200(我认为它应该是202)。网址有效,但是 基本的HTTP身份验证似乎没有用,因为 缩短的网址不在我的网址列表中(http://tr.im/?page=1)。

阅读http://www.voidspace.org.uk/python/articles/authentication.shtml#doing-it-properly后 我也尝试过:

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

但我得到了同样的结果。 (response.code为200,url有效, 但未记录在http://tr.im/的帐户中。)

如果我使用查询字符串参数而不是基本的HTTP身份验证, 像这样:

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

...然后不仅url有效,而且它记录在我的tr.im帐户中。 (虽然response.code仍然是200。)

虽然我的代码一定有问题(而不是tr.im的API),因为

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

...返回:

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

...网址确实显示在http://tr.im/?page=1上的网址列表中。

如果我跑:

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

......再次,我明白了:

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

注意代码是201,消息是“tr.im URL已经创建[yacitus]。”

我不能正确地进行基本的HTTP身份验证(在任一种尝试中)。你能发现我的问题吗?也许我应该看一下,通过电线发送什么?我以前从未这样做过。我可以使用Python API(可能在pdb中)吗?或者我可以使用另一种工具(最好是Mac OS X)吗?

7 个答案:

答案 0 :(得分:240)

这似乎工作得很好(取自另一个主题)

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)

答案 1 :(得分:19)

非常便宜的解决方案:

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

(您可能认为不适合多种原因,例如网址的安全性)

Github API example

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

答案 2 :(得分:13)

查看this SO post answer并查看basic authentication tutorial中的urllib2 missing manual

为了使urllib2基本身份验证正常工作,http响应必须包含HTTP代码401未授权 密钥"WWW-Authenticate",其值为{{1}否则,Python将不会发送您的登录信息,您需要使用Requests"Basic"将您的登录信息添加到网址中,或者添加标题,如@Flowpoke's answer

您可以将urllib.urlopen(url)放入试用栏中来查看错误:

urlopen

答案 3 :(得分:6)

The recommended way将使用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

这是基于Python 2/3兼容urllib2的单一来源变体:

#!/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+ introduces HTTPPasswordMgrWithPriorAuth()允许:

  

..消除不必要的401响应处理,或者无条件地在第一个请求上发送凭据,以便与未返回404响应的服务器通信而不是401,如果未发送Authorization标头...

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

在这种情况下,如果有必要,可以很容易地将HTTPBasicAuthHandler()替换为ProxyBasicAuthHandler()

答案 4 :(得分:4)

我建议当前的解决方案是使用我的包urllib2_prior_auth,它很好地解决了这个问题(我在inclusion处理标准的lib。

答案 5 :(得分:3)

Python urllib2 Basic Auth Problem相同的解决方案适用。

https://stackoverflow.com/a/24048852/1733117;您可以将urllib2.HTTPBasicAuthHandler子类化为每个与已知网址匹配的请求添加Authorization标头。

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

答案 6 :(得分:0)