Python urllib2, 기본 HTTP 인증 및 tr.im


84

URL을 단축하기 위해 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이어야한다고 생각합니다). url은 유효하지만 단축 된 URL이 내 URL 목록 ( http://tr.im/?page=1 )에 없기 때문에 기본 HTTP 인증이 작동하지 않는 것 같습니다 .

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

... 그리고 URL이 http://tr.im/?page=1 의 URL 목록에 나타납니다 .

그리고 내가 실행하면 :

$ 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 권장)가 있습니까?


2
사이트는 "WWW-Authenticate"urllib2 (또는 httplib2)가 자격 증명을 보내기 전에 401을 반환 하고 코드화 해야합니다 . 아래 내 대답을 참조하십시오 .
Mark Mikofski

참고 :이 서비스는 작동하지 않는 것 같습니다.
Laurel

답변:


246

이것은 정말 잘 작동하는 것 같습니다 (다른 스레드에서 가져옴)

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)

7
대신 base64.encodestring 및 대체 사용 base64.standard_b64encode의
파블 Polewicz을

5
request.add_header('Authorization', b'Basic ' + base64.b64encode(username + b':' + password))
jfs

1
이 답변을 기반으로 stdlib 외부에 종속성이없는 urllib2_prior_auth 패키지를 만들었 으며 관련 변경 사항을 stdlib에 푸시 하려고합니다 .
mcepl 2014 년

5
또는 더 짧아 지거나 가져 오기 피하기 : request.add_header ( 'Authorization', b'Basic '+ (username + b': '+ password) .encode ('base64 '))
makapuf

20

정말 저렴한 솔루션 :

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

(URL 보안과 같은 여러 가지 이유로 적합하지 않다고 결정할 수 있습니다)

Github API 예 :

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

쿼리 문자열 매개 변수를 사용하는 것보다 이점이 있습니까?
Daryl Spitzer

1
Daryl : 작동한다면 장점이라고 말할 수 있습니다. 대부분의 http 클라이언트는 처리 방법에 대해 좀 더주의를 기울이기 때문에 쿼리 문자열 인수보다 더 안전합니다.
Ali Afshar

나는 아마 이것으로 갈 것입니다 (그러므로 당신은 내 찬성 투표를 얻습니다).하지만 여전히 내 코드에 무엇이 잘못되었는지 알고 싶습니다 (그래서 이것은 내가 받아 들인 대답이 아닙니다).
Daryl Spitzer

36
이 반환 오류 ... InvalidURL : 숫자가 아닌 포트 : 'xxxx@api.tr.im/api'
닉 볼튼

5
당신이 urllib2.urlopen (URL)를 사용하지 않는 확인 @nbolton
CantGetANick

13

한 번 봐 가지고 이 SO 게시물에 대답을 하고 또한 이것 좀 봐 기본 인증 튜토리얼 으로부터 수동없는 urllib2가 .

작업에 urllib2가 기본 인증을 위해 HTTP 응답은 HTTP 코드 401 권한이 있어야합니다 핵심 "WWW-Authenticate"가치로 "Basic", 그렇지 않으면, 파이썬은 로그인 정보를 전송하지 않습니다, 그리고 당신도 사용에 필요한 요청 또는 urllib.urlopen(url)에 로그인으로 url 또는 @Flowpoke의 답변 과 같은 헤더를 추가하십시오 .

urlopentry 블록 에 넣어 오류를 볼 수 있습니다 .

try:
    urllib2.urlopen(urllib2.Request(url))
except urllib2.HTTPError, e:
    print e.headers
    print e.headers.has_key('WWW-Authenticate')

헤더를 인쇄하면 인증 영역을 잘못 입력했음을 깨닫게되므로 도움이되었습니다. +1
freespace

7

권장되는 방법requests모듈 을 사용하는 입니다 .

#!/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 이상에서는 다음HTTPPasswordMgrWithPriorAuth() 을 허용합니다.

.. 불필요한 401 응답 처리를 제거하거나 Authorization 헤더가 전송되지 않은 경우 401 대신 404 응답을 반환하는 서버와 통신하기 위해 첫 번째 요청에서 무조건 자격 증명을 전송합니다.

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

나는 현재 솔루션 내 패키지 사용하는 것을 제안 urllib2_prior_auth 에이 꽤 잘 (I 사업 해결 을 포함 표준 lib 디렉토리에 있습니다.


1
Python 3.5에 다음과 같이 포함되었습니다.urrlib.request.HTTPBasicPriorAuthHandler
mcepl

3

Python urllib2 기본 인증 문제 와 동일한 솔루션이 적용됩니다.

참조 https://stackoverflow.com/a/24048852/1733117을 ; 알려진 URL과 일치하는 각 요청에 헤더 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

strip이후 중복 호출이 b64encode아닌가요?
Mihai Todor

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.