因為最近接觸到調用新浪微博開放介面的項目,所以就想試試用python調用微博API。
SDK下載地址:http://open.weibo.com/wiki/SDK 代碼不多十幾K,完全可以看懂。
有微博帳號可以建立一個APP,然後就可以得到app key和app secret,這個是APP獲得OAuth2.0授權所必須的。
瞭解OAuth2可以查看連結新浪微博的說明。 OAuth2授權參數除了需要app key和app secret還需要網站回調地址redirect_uri,並且這個回調地址不允許是區域網路的(神馬localhost,127.0.0.1好像都不行),這個著實讓我著急了半天。我使用API也不是網站調用,於是查了很多。看到有人寫可以用這個地址替代,https://api.weibo.com/oauth2/default.html,我試了一下果然可以,對於屌絲來說是個好訊息。
下面先來個簡單的程式,感受一下:
設定好以下參數
import sysimport weiboimport webbrowserAPP_KEY = ''MY_APP_SECRET = ''REDIRECT_URL = 'https://api.weibo.com/oauth2/default.html'
獲得微博授權URL,如第2行,用預設瀏覽器開啟後會要求登陸微博,用需要授權的帳號登陸,如
api = weibo.APIClient(app_key=APP_KEY,app_secret=MY_APP_SECRET,redirect_uri=REDIRECT_URL)authorize_url = api.get_authorize_url()print(authorize_url)webbrowser.open_new(authorize_url)
登陸後會調轉到一個串連https://api.weibo.com/oauth2/default.html?code=92cc6accecfb5b2176adf58f4c
關鍵就是code值,這個是認證的關鍵。手動輸入code值類比認證
request = api.request_access_token(code, REDIRECT_URL)access_token = request.access_tokenexpires_in = request.expires_inapi.set_access_token(access_token, expires_in)api.statuses.update.post(status=u'Test OAuth 2.0 Send a Weibo!')
access_token就是獲得的token,expires_in是授權的到期時間 (UNIX時間)
用set_access_token儲存授權。往下就可以調用微博介面了。測試發了一條微博
但是這樣的手動輸入code方式,不適合程式的調用,是否可以不用開啟連結的方式來請求登陸擷取授權,經多方尋找和參考,將程式改進如下,可以實現自動擷取code並儲存,方便程式服務調用。
accessWeibo# -*- coding: utf-8 -*- #/usr/bin/env python #access to SinaWeibo By sinaweibopy #實現微博自動登入,token自動產生,儲存及更新 #適合於後端服務調用 from weibo import APIClient import pymongo import sys, os, urllib, urllib2 from http_helper import * from retry import * try: import json except ImportError: import simplejson as json # setting sys encoding to utf-8 default_encoding = 'utf-8' if sys.getdefaultencoding() != default_encoding: reload(sys) sys.setdefaultencoding(default_encoding) # weibo api訪問配置 APP_KEY = '' # app key APP_SECRET = '' # app secret REDIRECT_URL = 'https://api.weibo.com/oauth2/default.html' # callback url 授權回調頁,與OAuth2.0 授權設定的一致 USERID = '' # 登陸的微博使用者名稱,必須是OAuth2.0 設定的測試帳號 USERPASSWD = '' # 使用者密碼 client = APIClient(app_key=APP_KEY, app_secret=APP_SECRET, redirect_uri=REDIRECT_URL) def make_access_token(): #請求access token params = urllib.urlencode({'action':'submit','withOfficalFlag':'0','ticket':'','isLoginSina':'', 'response_type':'code','regCallback':'','redirect_uri':REDIRECT_URL,'client_id':APP_KEY,'state':'','from':'','userId':USERID,'passwd':USERPASSWD,}) login_url = 'https://api.weibo.com/oauth2/authorize' url = client.get_authorize_url() content = urllib2.urlopen(url) if content: headers = { 'Referer' : url } request = urllib2.Request(login_url, params, headers) opener = get_opener(False) urllib2.install_opener(opener) try: f = opener.open(request) return_redirect_uri = f.url except urllib2.HTTPError, e: return_redirect_uri = e.geturl() # 取到返回的code code = return_redirect_uri.split('=')[1] #得到token token = client.request_access_token(code,REDIRECT_URL) save_access_token(token) def save_access_token(token): #將access token儲存到MongoDB資料庫mongoCon=pymongo.Connection(host="127.0.0.1",port=27017)db= mongoCon.weibot={"access_token":token['access_token'],"expires_in":str(token['expires_in']),"date":time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))}db.token.insert(t,safe=True) #Decorator 目的是當調用make_access_token()後再執行一次apply_access_token()@retry(1) def apply_access_token(): #從MongoDB讀取及設定access token try: mongoCon=pymongo.Connection(host="127.0.0.1",port=27017)db= mongoCon.weiboif db.token.count()>0:tokenInfos=db.token.find().sort([("_id",pymongo.DESCENDING)]).limit(1)else: make_access_token() return False for tokenInfo in tokenInfos:access_token=tokenInfo["access_token"]expires_in=tokenInfo["expires_in"]try: client.set_access_token(access_token, expires_in) except StandardError, e: if hasattr(e, 'error'): if e.error == 'expired_token': # token到期重建 make_access_token()return False else: pass except: make_access_token()return False return True if __name__ == "__main__": apply_access_token() # 以下為訪問微博api的應用邏輯 # 以發布文字微博介面為例client.statuses.update.post(status='Test OAuth 2.0 Send a Weibo!')
retry.pyimport mathimport time# Retry decorator with exponential backoffdef retry(tries, delay=1, backoff=2):"""Retries a function or method until it returns True.delay sets the initial delay, and backoff sets how much the delay shouldlengthen after each failure. backoff must be greater than 1, or else itisn't really a backoff. tries must be at least 0, and delay greater than0."""if backoff <= 1:raise ValueError("backoff must be greater than 1")tries = math.floor(tries)if tries < 0:raise ValueError("tries must be 0 or greater")if delay <= 0:raise ValueError("delay must be greater than 0")def deco_retry(f):def f_retry(*args, **kwargs):mtries, mdelay = tries, delay # make mutablerv = f(*args, **kwargs) # first attemptwhile mtries > 0:if rv == True or type(rv) == str: # Done on success ..return rvmtries -= 1 # consume an attempttime.sleep(mdelay) # wait...mdelay *= backoff # make future wait longerrv = f(*args, **kwargs) # Try againreturn False # Ran out of tries :-(return f_retry # true decorator -> decorated functionreturn deco_retry # @retry(arg[, ...]) -> true decorator
http_helper.py# -*- coding: utf-8 -*-#/usr/bin/env pythonimport urllib2,cookielibclass SmartRedirectHandler(urllib2.HTTPRedirectHandler):def http_error_301(cls, req, fp, code, msg, headers):result = urllib2.HTTPRedirectHandler.http_error_301(cls, req, fp, code, msg, headers)result.status = codeprint headersreturn resultdef http_error_302(cls, req, fp, code, msg, headers):result = urllib2.HTTPRedirectHandler.http_error_302(cls, req, fp, code, msg, headers)result.status = codeprint headersreturn resultdef get_cookie():cookies = cookielib.CookieJar()return urllib2.HTTPCookieProcessor(cookies)def get_opener(proxy=False):rv=urllib2.build_opener(get_cookie(), SmartRedirectHandler())rv.addheaders = [('User-agent', 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)')]return rv