2009-03-11 11 views
73

Tôi đang phát xung quanh, cố gắng viết một số mã để sử dụng các API tr.im để rút ngắn URL.Python urllib2, xác thực HTTP cơ bản và tr.im

Sau khi đọc http://docs.python.org/library/urllib2.html, tôi đã cố gắng:

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 là 200 (Tôi nghĩ rằng nó phải 202). url là hợp lệ, nhưng xác thực HTTP cơ bản dường như không hoạt động, vì URL rút gọn không có trong danh sách URL của tôi (tại http://tr.im/?page=1).

Sau khi đọc http://www.voidspace.org.uk/python/articles/authentication.shtml#doing-it-properly Tôi cũng đã cố gắng:

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

Nhưng tôi nhận được kết quả tương tự. (Response.code là 200 và url là hợp lệ, nhưng không được ghi lại trong tài khoản của tôi tại http://tr.im/.)

Nếu tôi sử dụng các thông số chuỗi truy vấn thay vì xác thực HTTP cơ bản, như thế này:

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

. ..đây không chỉ là url hợp lệ mà nó được ghi lại trong tài khoản tr.im của tôi. (Mặc dù response.code vẫn 200. là)

Phải có một cái gì đó sai với mã của tôi mặc dù (và không tr.im của API), vì

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

... lợi nhuận:

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

... và URL xuất hiện trong danh sách URL của tôi trên http://tr.im/?page=1.

Và nếu tôi chạy:

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

... một lần nữa, tôi nhận được:

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

Lưu ý code is 201, và thông điệp là "URL tr.im Đã tạo [yacitus]. "

Tôi không được thực hiện xác thực HTTP cơ bản một cách chính xác (trong cả hai lần thử). Bạn có thể phát hiện vấn đề của tôi không? Có lẽ tôi nên xem và xem những gì đang được gửi qua dây? Tôi chưa bao giờ làm điều đó trước đây. Có Python API tôi có thể sử dụng (có lẽ trong pdb)? Hoặc là có một công cụ (tốt cho Mac OS X) tôi có thể sử dụng?

+2

trang web phải trả về "WWW-Authenticate" 'và mã 401 trước urllib2 (hoặc httplib2) sẽ gửi thông tin đăng nhập của bạn. [Xem câu trả lời của tôi bên dưới] (http://stackoverflow.com/questions/635113/python-urllib2-basic-http-authentication-and-tr-im/9698319#9698319). –

+0

Lưu ý: Dịch vụ này có vẻ không còn tồn tại. – Laurel

Trả lời

229

Điều này có vẻ để hoạt động thực sự tốt (được lấy từ một chủ đề khác)

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) 
+3

[ source] (http://stackoverflow.com/a/2955687/708764) – seler

+7

Thay vì base64.encodestring và thay thế, hãy sử dụng base64.standard_b64encode –

+5

'request.add_header ('Authorization', b'Basic '+ base64.b64encode (tên người dùng + b ':' + mật khẩu)) ' – jfs

18

giải pháp thực sự rẻ:

urllib.urlopen('http://user:[email protected]/api') 

(mà bạn có thể quyết định là không thích hợp cho một số lý do, như an ninh của url)

Github API example:

>>> import urllib, json 
>>> result = urllib.urlopen('https://personal-access-token:[email protected]/repos/:owner/:repo') 
>>> r = json.load(result.fp) 
>>> result.close() 
+0

Có bất kỳ lợi thế nào đối với việc sử dụng thông số chuỗi truy vấn này không? –

+0

Daryl: nếu nó hoạt động, tôi sẽ nói rằng đó là một lợi thế có, và có lẽ an toàn hơn đối số chuỗi truy vấn vì hầu hết các máy khách http cẩn thận hơn một chút về cách chúng xử lý chúng. –

+0

Tôi có thể sẽ đi với điều này (vì vậy bạn nhận được upvote của tôi), nhưng tôi vẫn muốn tìm ra những gì sai với mã của tôi (vì vậy đây sẽ không được trả lời của tôi chấp nhận). –

12

Hãy xem this SO post answer và cũng xem tại đây basic authentication tutorial từ urllib2 missing manual.

Để cho xác thực cơ bản urllib2 để làm việc, phản ứng http phải chứa mã HTTP 401 Unauthorized một chìa khóa "WWW-Authenticate" với giá trị "Basic" khác, Python sẽ không gửi thông tin đăng nhập của bạn, và bạn sẽ cần phải hoặc sử dụng Requests hoặc urllib.urlopen(url) bằng thông tin đăng nhập của bạn trong url hoặc thêm tiêu đề như trong @Flowpoke'sanswer.

Bạn có thể xem lỗi của bạn bằng cách đặt urlopen của bạn trong một khối try:

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

Điều này đã giúp tôi bởi vì in các tiêu đề dẫn tôi đến nhận ra tôi đã đánh máy lĩnh vực xác thực. +1 – freespace

2

giải pháp Tương tự như Python urllib2 Basic Auth Problem áp dụng.

xem https://stackoverflow.com/a/24048852/1733117; bạn có thể phân lớp urllib2.HTTPBasicAuthHandler để thêm tiêu đề Authorization vào mỗi yêu cầu khớp với url đã biết.

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 
+0

Không phải là cuộc gọi đến 'strip' dư thừa sau' b64encode'? –

2

tôi sẽ đề nghị rằng các giải pháp hiện nay là sử dụng gói của tôi urllib2_prior_auth mà giải quyết này khá độc đáo (tôi làm việc trên inclusion vào thư mục lib chuẩn

6

The recommended way là sử dụng 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 

Đây là một nguồn Python 2/3 tương thích urllib2 biến thể dựa trên:

#!/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+ introduces HTTPPasswordMgrWithPriorAuth() cho phép:

..to loại bỏ xử lý 401 phản ứng không cần thiết, hoặc để vô điều kiện gửi thông tin về yêu cầu đầu tiên để giao tiếp với các máy chủ trả lại một phản ứng 404 thay vì một 401 nếu Authorization tiêu đề không được gửi ..

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

Nó rất dễ dàng để thay thế HTTPBasicAuthHandler() với ProxyBasicAuthHandler() nếu cần thiết trong trường hợp này.