Python urllib2, базовая http-аутентификация и tr.im


Я играю вокруг, пытаясь написать какой-то код, чтобы использовать tr.im API для сокращения URL-адреса.

после прочтения 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()

ответ.код 200 (я думаю, что это должно быть 202). URL-адрес является допустимым, но базовая аутентификация HTTP, похоже, не сработала, потому что сокращенный URL-адрес отсутствует в моем списке URL-адресов (в 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()

но я получаю те же результаты. (ответ.код 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 счет. (Хотя ответ.код это еще 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-адрес появляется в моем списке URL-адресов на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 правильно (в любой попытке). Вы можете определить мою проблему? Может быть, мне стоит посмотреть, что посылают по проводам? Я никогда не делал этого раньше. Есть ли API Python, которые я могу использовать (возможно, в pdb)? Или есть другой инструмент (предпочтительно для Mac OS X), который я могу использовать?

7 78

7 ответов:

это, кажется, работает очень хорошо (взято из другого потока)

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)

пример API Github:

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

посмотри это так пост ответ и посмотрите на это обычная проверка подлинности учебник С urllib2 отсутствует руководство.

для работы обычной проверки подлинности urllib2 ответ http должен содержать код HTTP 401 Unauthorized и ключ "WWW-Authenticate" со значением "Basic" в противном случае Python не будет отправлять вашу регистрационную информацию, и вам нужно будет либо использовать запросы, или urllib.urlopen(url) С вашим логином в url, или добавить заголовок, как в @Flowpoke этоответ.

вы можете просмотреть свою ошибку, поставив свой urlopen в блоке try:

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 или безоговорочно отправить учетные данные по первому запросу для связи с серверами, которые возвращают ответ 404 Вместо ответа 401 если заголовок авторизации не отправлен..

#!/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() при необходимости в этом случае.

те же решения, что и Python urllib2 основная проблема аутентификации применить.

см.https://stackoverflow.com/a/24048852/1733117; Вы можете подкласс urllib2.HTTPBasicAuthHandler добавить Authorization заголовок для каждого запроса, который соответствует известному url.

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

Я бы предположил, что текущее решение заключается в использовании моего пакета urllib2_prior_auth который решает это довольно хорошо (я работаю над включение к стандартной lib.

попробовать python-request или python-grab