天天看點

Python模拟新浪微網誌登入

本文轉自:http://www.jb51.net/article/44779.htm

1、主函數(WeiboLogin.py)

import urllib2
import cookielib
import WeiboEncode
import WeiboSearch

if __name__ == '__main__':
    weibologin = WeiboLogin('xxxxxxx', 'xxxxxx')#使用者名、密碼
    if weibologin.Login() == True:
        print "登陸成功!"      

前兩個import是加載Python的網絡程式設計子產品,後面的import是加載另兩個檔案WeiboEncode.py和Weiboseach.py(稍後介紹)。主函數建立登陸對象,然後登陸。

2、WeiboLogin類(WeiboLogin.py):

class WeiboLogin:
    def __init__(self, user, pwd, enableProxy = False):
        "初始化WeiboLogin,enableProxy表示是否使用代理伺服器,預設關閉"
        print "Initializing WeiboLogin..."
        self.userName = user
        self.passWord = pwd
        self.enableProxy = enableProxy
        
        self.serverUrl = "http://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su=&rsakt=mod&client=ssologin.js(v1.4.18)&_=1407721000736"
        self.loginUrl = "http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.18)"
        self.postHeader = {'User-Agent': 'Mozilla/5.0 (X11; Linux i686; rv:24.0) Gecko/20140611 Firefox/24.0 Iceweasel/24.6.0'}      

始化使用者名,密碼,serverUrl用于第一步擷取servertime,nonce等。loginUrl用于第二步,加密後的使用者名和密碼POST給

這個URL。關于這些步驟是怎麼知道的?其實這些資料的傳輸都可以監視到。像我用的firefox中有一個Httpfox插件就有這個功能。

3、類内的函數還有:

    def Login(self):
        "登陸程式"
        self.EnableCookie(self.enableProxy)#cookie或代理伺服器配置
            serverTime, nonce, pubkey, rsakv = self.GetServerTime()#登陸的第一步
            postData = WeiboEncode.PostEncode(self.userName, self.passWord, serverTime, nonce, pubkey, rsakv)#加密使用者和密碼
            #print "Post data length:\n", len(postData)
        
        req = urllib2.Request(self.loginUrl, postData, self.postHeader)
            #print "Posting request..."
        result = urllib2.urlopen(req)#登陸的第二步
        text = result.read()
        try:
            loginUrl = WeiboSearch.sRedirectData(text)#解析重定位結果
            urllib2.urlopen(loginUrl)
        except:
            print 'Login error!'
            return False
        
        #print 'Login sucess!'
        return True      

self.EnableCookie用于設定cookie及代理伺服器,網絡上有很多免費的代理伺服器,為防止新浪封IP,可以使用。關于代理IP的方法,我試過會出現登入次數過多的錯誤。然後是登陸的第一

步,通路新浪伺服器得到serverTime等資訊,然後利用這些資訊加密使用者名和密碼,建構POST請求;執行第二步,向self.loginUrl發

送使用者和密碼,得到重定位資訊後,解析得到最終跳轉到的URL,打開該URL後,伺服器自動将使用者登陸資訊寫入cookie,登陸成功。

    def EnableCookie(self, enableProxy):
        "Enable cookie & proxy (if needed)."
        cookiejar = cookielib.LWPCookieJar()#建立cookie
        cookie_support = urllib2.HTTPCookieProcessor(cookiejar)
        if enableProxy:
            proxy_support = urllib2.ProxyHandler({'http':'http://122.96.59.107:843'})#使用代理
            opener = urllib2.build_opener(proxy_support, cookie_support, urllib2.HTTPHandler)
            print "Proxy enabled"
        else:
            opener = urllib2.build_opener(cookie_support, urllib2.HTTPHandler)        
        
        
        urllib2.install_opener(opener)#建構cookie對應的opener      
    def GetServerTime(self):
        "Get server time and nonce, which are used to encode the password"
        
        #print "Getting server time and nonce..."
        
        serverData = urllib2.urlopen(self.serverUrl).read()#得到網頁内容
        #print serverData
        
        try:
            serverTime, nonce, pubkey, rsakv = WeiboSearch.sServerData(serverData)#解析得到serverTime,nonce等
            return serverTime, nonce, pubkey, rsakv
        except:
            print 'Get server time & nonce error!'
            return None      

4、sSearch函數(WeiboSearch.py):

def sServerData(serverData):
    "Search the server time & nonce from server data"
    p = re.compile('\((.*)\)')
    jsonData = p.search(serverData).group(1)
    data = json.loads(jsonData)
    serverTime = str(data['servertime'])
    nonce = data['nonce']
    pubkey = data['pubkey']
    rsakv = data['rsakv']
#    print "Server time is:", serverTime
#    print "Nonce is:", nonce
    return serverTime, nonce, pubkey, rsakv      
解析過程主要使用了正規表達式和JSON。另外Login中解析重定位結果部分函數也在這個檔案中如下:      
def sRedirectData(text):
    p = re.compile('location\.replace\([\'"](.*?)[\'"]\)')
    loginUrl = p.search(text).group(1)
    print 'loginUrl:',loginUrl
    return loginUrl      

5、WeiboEncode.py:

import urllib
import base64
import rsa
import binascii

def PostEncode(userName, passWord, serverTime, nonce, pubkey, rsakv):
    "Used to generate POST data"
    encodedUserName = GetUserName(userName)#使用者名使用base64加密
    encodedPassWord = get_pwd(passWord, serverTime, nonce, pubkey)#目前密碼采用rsa加密
    postPara = {
            'entry': 'weibo',
            'gateway': '1',
            'from': '',
            'savestate': '7',
            'userticket': '1',
            'ssosimplelogin': '1',
            'vsnf': '1',
            'vsnval': '',
            'su': encodedUserName,
            'service': 'miniblog',
            'servertime': serverTime,
            'nonce': nonce,
            'pwencode': 'rsa2',
            'sp': encodedPassWord,
            'encoding': 'UTF-8',
            'prelt': '115',
            'rsakv': rsakv,    
            'url': 'http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack',
            'returntype': 'META'
            }
    postData = urllib.urlencode(postPara)#網絡編碼
    return postData


def GetUserName(userName):
    "Used to encode user name"
    
    userNameTemp = urllib.quote(userName)
    userNameEncoded = base64.encodestring(userNameTemp)[:-1]
    return userNameEncoded


def get_pwd(password, servertime, nonce, pubkey):
    rsaPublickey = int(pubkey, 16)
    key = rsa.PublicKey(rsaPublickey, 65537) #建立公鑰
    message = str(servertime) + '\t' + str(nonce) + '\n' + str(password) #拼接明文js加密檔案中得到
    passwd = rsa.encrypt(message, key) #加密
    passwd = binascii.b2a_hex(passwd) #将加密資訊轉換為16進制。
    return passwd      

PostEncode函數建構POST的消息體,要求建構得到内容與真正登陸所需的資訊相同,這些可以通過Httpfox插件檢視。關于使用者名和密碼的加密,原來是SHA1,現在是RSA。

到這裡,模拟登入就成功了。輸出如下:

loginUrl: 
http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack&ssosavestate=1390390056&ticket=ST-MzQ4NzQ5NTYyMA==-1387798056-xd-284624BFC19FE242BBAE2C39FB3A8CA8&retcode=0
登入成功      
htmlContent = urllib2.urlopen(myurl).read()#得到myurl網頁的所有内容(html)