पायथन urllib2, मूल HTTP प्रमाणीकरण, और tr.im


84

मैं चारों ओर खेल रहा हूं, एक URL को छोटा करने के लिए tr.im APIs का उपयोग करने के लिए कुछ कोड लिखने की कोशिश कर रहा हूं ।

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 खाते में दर्ज है। (हालांकि प्रतिक्रिया.कोड अभी भी 200 है।)

मेरे कोड के साथ कुछ गलत होना चाहिए (और 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 है, और संदेश "tr.im URL पहले से ही निर्मित [yacitus] है।"

मुझे मूल HTTP प्रमाणीकरण सही तरीके से (या तो प्रयास में) नहीं करना चाहिए। क्या आप मेरी समस्या का समाधान कर सकते हैं? शायद मुझे देखना चाहिए और देखना चाहिए कि तार पर क्या भेजा जा रहा है? मैंने पहले कभी ऐसा नहीं किया। क्या पायथन एपीआई मैं उपयोग कर सकते हैं (शायद पीडीबी में)? या वहाँ एक और उपकरण है (अधिमानतः मैक ओएस एक्स के लिए) मैं उपयोग कर सकता हूं?


2
साइट को वापस लौटना चाहिए "WWW-Authenticate"और urllib2 (या canplib2) से पहले 401 को कोड करना होगा जो आपकी साख भेजेगा। नीचे मेरा जवाब देखें
मार्क मिकोफ़्स्की

नोट: यह सेवा दोषपूर्ण लगती है।
लॉरेल

जवाबों:


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
Paweole Polewicz

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

1
इस उत्तर के आधार पर मैंने एक पैकेज urllib2_prior_auth बनाया है जिसमें stdlib के बाहर कोई निर्भरता नहीं है, और मैं stdlib के लिए प्रासंगिक परिवर्तन को आगे बढ़ाने का प्रयास करता हूं
mcepl

5
या यहां तक कि छोटी / एक आयात से परहेज: (, b'Basic '+ (उपयोगकर्ता नाम + बी' 'प्राधिकरण': '+ पासवर्ड) .encode (' बेस 64 ')) request.add_header
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()

क्या क्वेरी स्ट्रिंग मापदंडों का उपयोग करने से इस पर कोई लाभ है?
डेरिल स्पिट्जर

1
डेरिल: यदि यह काम करता है, तो मैं कहूंगा कि यह एक फायदा है हाँ, और शायद क्वेरी स्ट्रिंग तर्कों की तुलना में अधिक सुरक्षित है क्योंकि अधिकांश http क्लाइंट थोड़ा अधिक सावधान हैं कि वे उन्हें कैसे संभालते हैं।
अली अफसर

मैं शायद इसके साथ जाऊंगा (ताकि आपको मेरा उत्थान मिले), लेकिन मैं अभी भी यह पता लगाना चाहूंगा कि मेरे कोड में क्या गलत है (इसलिए यह मेरा स्वीकृत जवाब नहीं होगा)।
डेरिल स्पिट्जर

36
यह एक त्रुटि देता है ... InvalidURL: nonnumeric port: 'xxxx@api.tr.im/api'
Nick Bolton

5
@nbolton सुनिश्चित करें कि आप urllib2.urlopen (url) का उपयोग नहीं कर रहे हैं
CantGetANick

13

इस SO पोस्ट उत्तर पर एक नज़र डालें और urllib2 लापता मैनुअल से इस मूल प्रमाणीकरण ट्यूटोरियल को भी देखें ।

काम करने के लिए urllib2 मूल प्रमाणीकरण के लिए, http प्रतिक्रिया में HTTP कोड 401 अनधिकृत होना चाहिए और"WWW-Authenticate" मूल्य के साथ एक कुंजी होनी चाहिए "Basic"अन्यथा, पायथन आपकी लॉगिन जानकारी नहीं भेजेगा, और आपको अनुरोधों का उपयोग करने की आवश्यकता होगी , या urllib.urlopen(url)आपके लॉगिन के साथ। url, या @ फ़्लोपोक के उत्तर में हेडर जोड़ें ।

आप अपने urlopenप्रयास को ब्लॉक में रखकर अपनी त्रुटि देख सकते हैं :

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

इससे मुझे मदद मिली क्योंकि हेडर छापने से मुझे एहसास होता है कि मैंने प्रमाणीकरण क्षेत्र को टाइप किया था। +1
फ्रीस्पैस

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

यहाँ एक एकल स्रोत पायथन 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()

पायथन 3.5+ का परिचयHTTPPasswordMgrWithPriorAuth() देता है जो अनुमति देता है:

.. अनावश्यक 401 प्रतिसाद हैंडलिंग को समाप्त करने के लिए, या पहले से अनुरोध पर क्रेडेंशियल्स भेजने के लिए सर्वर के साथ संवाद करने के लिए जो 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()इस मामले में यदि आवश्यक हो तो।



3

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

क्या इसके stripबाद कॉल निरर्थक नहीं है b64encode?
मिहाई टोडर

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.