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

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



답변

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

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)


답변

정말 저렴한 솔루션 :

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


답변

한 번 봐 가지고 이 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')


답변

권장되는 방법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()이 경우 필요한 경우.


답변

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


답변

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


답변

python-request 또는 python-grab 시도