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()
response.code - 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()
Но я получаю те же результаты. (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.)
В моем коде должно быть что-то неправильное (а не API tr.im), потому что
$ 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, а сообщение - "URL-адрес tr.im уже создан [yacitus]."
Я не должен правильно выполнять базовую HTTP-аутентификацию (в любой попытке). Можете ли вы определить мою проблему? Возможно, я должен посмотреть и посмотреть, что отправляется по проводам? Я никогда этого не делал раньше. Существуют ли API-интерфейсы Python, которые я могу использовать (возможно, в pdb)? Или есть другой инструмент (желательно для Mac OS X), который я могу использовать?
Ответы
Ответ 1
Это, кажется, работает очень хорошо (взято из другого потока)
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)
Ответ 2
Действительно дешевое решение:
urllib.urlopen('http://user:[email protected]/api')
(который вы можете решить не подходит по ряду причин, таких как безопасность URL-адреса)
Пример API Github:
>>> import urllib, json
>>> result = urllib.urlopen('https://personal-access-token:[email protected]/repos/:owner/:repo')
>>> r = json.load(result.fp)
>>> result.close()
Ответ 3
Посмотрите этот ответ SO post, а также посмотрите базовый учебник по аутентификации из urllib2 отсутствует руководство.
Для того, чтобы базовая аутентификация urllib2 работала, HTTP-ответ должен содержать HTTP-код 401 Unauthorized и ключ "WWW-Authenticate"
со значением "Basic"
иначе, Python не отправит вашу регистрационную информацию, и вам нужно будет либо использовать Requests, либо urllib.urlopen(url)
с вашим логином в URL-адресе или добавить заголовок, как в @Flowpoke answer.
Вы можете просмотреть свою ошибку, поставив urlopen
в блок try:
try:
urllib2.urlopen(urllib2.Request(url))
except urllib2.HTTPError, e:
print e.headers
print e.headers.has_key('WWW-Authenticate')
Ответ 4
Рекомендуемым способом является использование 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+ представляет 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()
, если это необходимо в этом случае.
Ответ 5
Используются те же решения, что и Python urllib2 Базовая проблема аутентификации.
см. fooobar.com/questions/74103/...; вы можете подклассом 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
Ответ 6
Я бы предположил, что текущее решение заключается в использовании моего пакета urllib2_prior_auth, который решает это довольно красиво (я работаю над включение в стандартную библиотеку.
Ответ 7
Попробуйте python-request или python-grab