Python urllib2, temel HTTP kimlik doğrulaması ve tr.im


85

Bir URL'yi kısaltmak için tr.im API'lerini kullanmak için biraz kod yazmaya çalışıyorum .

Http://docs.python.org/library/urllib2.html okuduktan sonra şunu denedim:

   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'dür (202 olması gerektiğini düşünüyorum). url geçerli, ancak temel HTTP kimlik doğrulaması işe yaramadı, çünkü kısaltılmış URL, URL listemde bulunmuyor ( http://tr.im/?page=1 adresinde ).

Http://www.voidspace.org.uk/python/articles/authentication.shtml#doing-it-properly'yi okuduktan sonra şunu da denedim:

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

Ama aynı sonuçları alıyorum. (response.code 200 ve url geçerli, ancak http://tr.im/ adresindeki hesabıma kaydedilmemiş .)

Temel HTTP kimlik doğrulaması yerine sorgu dizesi parametreleri kullanırsam, şunun gibi:

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

... o zaman sadece url geçerli değil, tr.im hesabıma kaydediliyor. (Response.code hala 200 olmasına rağmen.)

Yine de kodumda bir sorun olmalı (ve tr.im API'sinde değil), çünkü

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

...İadeler:

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

... ve URL http://tr.im/?page=1 adresindeki URL listemde görünüyor .

Ve koşarsam:

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

... yine anlıyorum:

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

Not kodu 201'dir ve mesaj "tr.im URL'si Zaten Oluşturuldu [yacitus]" şeklindedir.

Temel HTTP kimlik doğrulamasını doğru yapmamalıyım (her iki denemede de). Benim sorunumu anlıyor musun? Belki de tel üzerinden neyin gönderildiğine bakmalıyım? Bunu daha önce hiç yapmadım. Kullanabileceğim Python API'leri var mı (belki pdb'de)? Veya kullanabileceğim başka bir araç var mı (tercihen Mac OS X için)?


2
"WWW-Authenticate"urllib2 (veya httplib2) kimlik bilgilerinizi göndermeden önce site 401 döndürmeli ve kodlamalıdır. Aşağıdaki cevabıma bakın .
Mark Mikofski

Not: Bu hizmetin geçersiz olduğu görülüyor.
Laurel

Yanıtlar:


247

Bu gerçekten iyi çalışıyor gibi görünüyor (başka bir ileti dizisinden alınmıştır)

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 ve replace yerine base64.standard_b64encode kullanın
Paweł Polewicz

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

1
Bu yanıta dayanarak stdlib dışında bağımlılığı olmayan bir urllib2_prior_auth paketi oluşturdum ve ilgili değişikliği stdlib'e göndermeye çalışıyorum .
mcepl

5
Veya daha da kısası / içe aktarmadan kaçınmak: request.add_header ('Yetkilendirme', b'Basic '+ (kullanıcı adı + b': '+ şifre) .encode (' base64 '))
makapuf

20

Gerçekten ucuz çözüm:

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

(URL'nin güvenliği gibi bir dizi nedenden dolayı uygun olmadığına karar verebilirsiniz)

Github API örneği :

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

Bunun sorgu dizisi parametrelerini kullanmaya göre herhangi bir avantajı var mı?
Daryl Spitzer

1
Daryl: Eğer işe yararsa, bunun bir avantaj olduğunu söyleyebilirim ve muhtemelen çoğu http istemcisi bunları nasıl ele aldıkları konusunda biraz daha dikkatli olduğundan sorgu dizesi argümanlarından daha güvenli.
Ali Afşar

Muhtemelen bununla devam edeceğim (bu yüzden oyumu alacaksınız), ancak yine de kodumda neyin yanlış olduğunu bulmak istiyorum (bu yüzden bu benim kabul edilen cevabım olmayacak).
Daryl Spitzer

36
Bu bir hata döndürür ... InvalidURL: sayısal olmayan bağlantı noktası: 'xxxx@api.tr.im/api'
Nick Bolton

5
@nbolton urllib2.urlopen (url) kullanmadığınızdan emin olun
CantGetANick

13

Bu SO gönderi yanıtına bir göz atın ve urllib2 eksik kılavuzundaki bu temel kimlik doğrulama eğitimine de bakın .

Urllib2 temel kimlik doğrulamasının çalışması için, http yanıtı HTTP kodu 401 Unauthorized ve"WWW-Authenticate" değerine sahip bir anahtar içermelidir "Basic", aksi takdirde Python oturum açma bilgilerinizi göndermez ve ya Requests'i ya urllib.urlopen(url)da url, ya da benzer bir başlığı eklemek Flowpoke en @ cevap .

Eğer koyarak hatayı görebilirsiniz urlopenbir try bloğunda:

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

Bu bana yardımcı oldu, çünkü başlıkları yazdırmak, kimlik doğrulama alanında yazım hatası yaptığımı fark etmeme yol açtı. +1
boş alan

7

Önerilen yol , requestsmodülü kullanmaktır :

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

İşte Python 2/3 uyumlu tek bir kaynak urllib2varyantı:

#!/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() şunları sağlayan sunar:

.. Gereksiz 401 yanıtı işlemeyi ortadan kaldırmak veya Yetkilendirme başlığı gönderilmezse 401 yanıtı yerine 404 yanıtı döndüren sunucularla iletişim kurmak için ilk istekte koşulsuz olarak kimlik bilgileri göndermek ..

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

Bu durumda gerekirse HTTPBasicAuthHandler()ile değiştirmek kolaydır ProxyBasicAuthHandler().


4

Şu anki çözümün, bunu oldukça güzel bir şekilde çözen urllib2_prior_auth paketimi kullanmak olduğunu öneririm ( standart lib'ye dahil etmek için çalışıyorum .


1
Python 3.5'e dahil edildiurrlib.request.HTTPBasicPriorAuthHandler
mcepl

3

Python urllib2 Temel Kimlik Doğrulama Problemi ile aynı çözümler geçerlidir.

bkz. https://stackoverflow.com/a/24048852/1733117 ; bilinen url ile eşleşen her bir isteğe üstbilgi urllib2.HTTPBasicAuthHandlereklemek için alt sınıfa ayırabilirsiniz 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

stripSonra çağrı gereksiz değil b64encodemi?
Mihai Todor

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.