花了4個晚上終于把模擬登錄新浪微博學習完了,相對于知乎迷你登錄仲智,微博登錄的過程確實難度大了很多莱衩,好多知識點都不懂烈钞,所以雖然把代碼都碼了一遍贫堰,但很多都是照貓畫虎,其實還有很多地方不是十分清楚考传。代碼中寫了很多注解(對向我一樣的初學者來說應該很有必要),所以看以來不是很簡潔证鸥,其實源碼查不到150行僚楞。
希望本文能對向我一樣的初學者能起到一點借鑒作用
寫以下我這幾天自學的過程:
一、第一天直接看xchaoinfo的源碼枉层,完全看不懂泉褐,好不好∧窭看了15分鐘直接放棄膜赃,轉(zhuǎn)百度看圖文教程,還好網(wǎng)上的教程不算少(其實方法都一樣矩欠,代碼也基本沒多少差別财剖,應該起初都出自同一作者)悠夯,
網(wǎng)上從抓包介紹,好吧又不會躺坟,那就學吧~~~
這樣一天就過去了沦补,感覺什么都沒學到
二、第二天繼續(xù)抓包咪橙,還是沒什么進展夕膀,期間換了各種抓包工具,都不太會用美侦,感覺網(wǎng)上介紹的看方法用這些抓包工具都沒發(fā)抓到产舞,這樣一天快要過去的時候,想想還是用瀏覽器的F12吧菠剩,這樣就用了火狐瀏覽器(因為之前一致用chrome的F12看源碼易猫,也抓不到js文件),下載了firebug具壮,用起來還是一頭霧水准颓,晚上想想還是睡覺吧,睡覺前又百度了firebug的用法棺妓,本來也沒抱希望有什么用攘已,可是突然奇跡發(fā)生了,多次嘗試了之后找到了教程中的js文件怜跑,太晚了样勃,睡覺!P苑摇峡眶!
自學真的很痛苦,可能很簡單的問題會難住好幾天批旺。
三幌陕、第三天,繼續(xù)看js文件汽煮,抓post的各種屬性搏熄,看著教程試著理解密碼加密的方法,差不多的時候繼續(xù)回去啃源碼暇赤,中間碰到不會的模塊google和自己用idle一點點測試心例,理解個大概繼續(xù)往下啃,啃到一般就睡覺了鞋囊。
四止后、第四天的繼續(xù)下面的代碼,后面的表單登錄相對簡單,因為之前有相關(guān)的學習經(jīng)驗译株,進過兩個小時瓜喇,終于測試成功了。
下面把源碼貼出來給自己留個紀念歉糜,也給需要的書友提供一點思路乘寒。
"""
新浪微博模擬登錄練習,本教程參考了很多網(wǎng)上的教程匪补,很多教程的源碼都差不多伞辛,本文的代碼主要修改自author : "xchaoinfo" github的源碼
新浪微博的模擬登錄對新手來說比較困難(我就是初學者,以前沒有任何編程基礎夯缺,因為對現(xiàn)在的工作狀態(tài)不滿意蚤氏,2017年開始自學python)
author : xcaojianhong
qq:1254798548
date:2017.02.21
"""
import time
import base64 #加密模塊
import rsa #加密模塊
import binascii #二進制模塊
import requests #是的,本次練習還是用的requests庫踊兜,本意是學習scrapy爬蟲竿滨,可是找不到模擬登錄的詳細的教程,只能先擱置了
import re #正則模塊捏境,不太會用
import random #我不是太理解為甚要rondom模塊
import http.cookiejar
from PIL import Image #這個只用到簡單的方法姐呐,高級的本人也不會
from urllib.parse import quote_plus #涉及編碼問題,需要用到該模塊
'''
如果沒有開啟登錄保護典蝌,不用輸入驗證碼就可以登錄
如果開啟登錄保護,需要輸入驗證碼头谜,PIL庫就是用了打開驗證碼的圖片用的骏掀,就只是打開,還是需要手動輸入的柱告,不是完成自動驗證
'''
# 構(gòu)造 Request headers截驮,這個不解釋了,很多基礎教程中都會有的际度,一般網(wǎng)站都有反爬蟲的機制葵袭,最常見的就是識別是不是瀏覽器訪問的,這個就是用來模擬瀏覽器的
user_agent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
headers = {
'User-Agent': user_agent
}
session = requests.session() #實例化一個session類乖菱,session類能夠自動處理cookies
session.cookies = http.cookiejar.LWPCookieJar(filename = 'cookies') #用于保存cookies坡锡,對爬取后需爬去有用
index_url = "http://weibo.com/login.php" #這個是微博登錄的原始的url
# 訪問初始頁面帶上 cookies
try:
session.get(index_url,headers=headers,timeout=2) #為什么用延時參數(shù)不太清楚,我自己寫的話就只寫excpet后面的一句
except:
session.get(index_url,headers=headers)
try:
input = raw_input #這個自行百度兩個input的差異窒所,因為我也不太清楚
except:
pass
# 下面開始困難了鹉勒,我光抓包就花了兩個晚上才看懂網(wǎng)上講的方法是怎么回事,期間換了很多抓包的工具吵取,最后還是覺得firebug好用
def get_su(username):
"""
1.輸入賬號禽额,會先在js中通過encodeURIComponent進行編碼,對應python的urllib.parse.quote_plus方法,問我怎么知道的脯倒,是因為源碼中的注釋說的
密碼的加密方法在ssologin.js文件中实辑,為我為什么知道,網(wǎng)上教程說的藻丢,自己分析的話我估計2天都不一定能找到剪撬。
在js里面有這樣的代碼:
username = sinaSSOEncoder.base64.encode(urlencode(username))
urlencode函數(shù)中用了encodeURIComponent編碼,具體代碼就不貼了郁岩,自己仔細看能找到婿奔。這里能看到用戶名用了base64進行加密
2.其實我也不知道怎么解釋,為什么要先對username_quote進行utf-8編碼问慎,然后才base64加密萍摊,照著寫就行了,我在python中測試不編碼加密會報錯如叼,具體原因不清楚
"""
username_quote = quote_plus(username) #詳細解釋看1
username_base64 = base64.b64encode(username_quote.encode('utf-8')) #詳細解釋看2
# print(username_base64.decode('utf-8')) 測試用
return username_base64.decode('utf-8') #解碼成utf-8格式字符串
# 預登陸獲得 servertime, nonce, pubkey, rsakv 這些屬性在后面密碼加密過程中需要用到
def server_data(su):
"""
3.通過抓包分析我們看到冰木,https://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack
&su=ODg4ODg4OA==&rsakt=mod&checkpin=1&client=ssologin.js(v1.4.18)&_=1487602221124這個url的response是一個json格式的文本,其中包含
servertime, nonce, pubkey, rsakv這些屬性笼恰,這正是我們需要的踊沸,&su=ODg4ODg4OA==,這個su并不是get_su的返回值社证,但是相近逼龟,直接用get_su的返回值不影響
另外這里用到了time模塊,用了生成時間戳追葡,1487602221124這個就是時間戳
"""
#get_su()解釋了那么多腺律,就是為了獲得su,雖然在這里su不那么重要(因為實際預登錄url中su并不是完全正確的),但是最后的post表單中需要用到su
pre_url = "http://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su="\
+ su + "&rsakt=mod&checkpin=1&client=ssologin.js(v1.4.18)&_=" + str(int(time.time() * 1000)) #為什么是這個url宜肉,詳細請看3
pre_data_res = session.get(pre_url, headers=headers)
sever_data = eval(pre_data_res.content.decode("utf-8").replace("sinaSSOController.preloginCallBack", '')) #這里用repalce替換
# print(sever_data) 測試用
return sever_data
# 這個是本次登錄中最難的地方匀钧,需要用到上面的參數(shù)
def get_password(password,servertime,nonce,pubkey):
"""
具體為什么需要這些參數(shù),還是要仔細分析之前提到的js文件谬返,里面有密碼加密的過程
request.servertime = me.servertime;
request.nonce = me.nonce;
request.pwencode = "rsa2";
request.rsakv = me.rsakv;
var RSAKey = new sinaSSOEncoder.RSAKey();
RSAKey.setPublic(me.rsaPubkey, "10001"); #rsaPubkey就是pubkey之斯,js的代碼就不貼了
password = RSAKey.encrypt([me.servertime, me.nonce].join("\t") + "\n" + password)
"""
rsapublickey = int(pubkey,16) #如果是16進制,則轉(zhuǎn)化為10進制
key = rsa.PublicKey(rsapublickey,65537) # '10001'轉(zhuǎn)化為10進制就是65537
message = str(servertime) + '\t' + str(nonce) + '\n' + str(password) # 仔細對著js代碼慢慢看
message = message.encode("utf-8")
#print(message) 測試用
passwd = rsa.encrypt(message, key) # 加密遣铝,為什么要用rsa佑刷,我也不知道,源代碼就用的翰蠢,我也不懂這個方法的具體的用途项乒,反正看著和js的代碼相似的,當成是python版本的翻譯看
#print(passwd) 測試用
passwd = binascii.b2a_hex(passwd) # 將加密信息轉(zhuǎn)換為16進制梁沧,post需要16進制ps檀何,猜測的,對編碼不太了解。
# print(passwd) 測試用
return passwd
# 獲得驗證碼频鉴,并下載后用PIL模塊自動打開栓辜,沒有安裝的話手動去爬蟲文件的目錄中手動打開圖片
def get_cha(pic):
"""
我沒有詳細分析需要驗證碼的收獲,因為我登錄新浪微博基本沒有碰到需要驗證碼的時候
下面用到了隨機數(shù)垛孔,我不是太理解藕甩,既然能用隨機數(shù),為什么不能用固定的數(shù)字周荐,反正都是我們自己構(gòu)造的一個數(shù)字
"""
cha_url = "http://login.sina.com.cn/cgi/pin.php?r="
cha_url = cha_url + str(int(random.random() * 100000000)) + "&s=0&p="
cha_url = cha_url + pcid
cha_page = session.get(cha_url, headers=headers)
with open("cha.jpg", 'wb') as f:
f.write(cha_page.content)
f.close()
try:
im = Image.open("cha.jpg")
im.show()
im.close()
except:
print(u"請到當前目錄下狭莱,找到驗證碼后輸入")
def login(username,password):
"""
激動人心的時候快要到了,上面作的一些準備工作就是為了獲得post的屬性
"""
su = get_su(username) #獲得加密的su
sever_data = server_data(su) #獲得server_data函數(shù)返回的字典
servertime = sever_data['servertime']
nonce = sever_data['nonce']
rsakv = sever_data["rsakv"]
pubkey = sever_data["pubkey"]
showpin = sever_data["showpin"] #這個參數(shù)的值關(guān)系到是否需要輸入驗證碼概作,0表示不需要腋妙,1表示需要
password_secret = get_password(password,servertime,nonce,pubkey) #獲得加密的sp
# su,sp,servertime,nonce,rsakv,sp屬性是變化的,其他的都可以寫死
postdata = {
'entry':'weibo',
'gateway':'1',
'from':'',
'savestate':'7',
'useticket':'1',
'pagerefer':"http://login.sina.com.cn/sso/logout.php?entry=miniblog&r=http%3A%2F%2Fweibo.com%2Flogout.php%3Fbackurl",
'wsseretry':'servertime_error',
'vsnf':'1',
'su':su,
'service':'miniblog',
'servertime':servertime,
'nonce':nonce,
'pwencode':'rsa2',
'rsakv':rsakv,
'sp':password_secret,
'sr':'1536*864',
'encoding':'UTF-8',
'prelt':'105',
'url':'http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack',
'returntype':'META'
}
login_url = 'http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.18)' #用于登錄的url讯榕,psot表單提交的url
if showpin == 0:
login_page = session.post(login_url, data=postdata, headers=headers)
else:
pcid = sever_data["pcid"]
get_cha(pcid)
postdata['door'] = input(u"請輸入驗證碼")
login_page = session.post(login_url, data=postdata, headers=headers)
login_loop = (login_page.content.decode("GBK")) #看網(wǎng)絡抓包骤素,知道是gbk編碼
#print(login_loop)
pa = r'location\.replace\([\'"](.*?)[\'"]\)' #這里的正則查相關(guān)的教程
loop_url = re.findall(pa, login_loop)[0]
#print(loop_url)
# 正常訪問login_page就結(jié)束了,但是微博喪心病狂的還需要一部跳轉(zhuǎn)愚屁,請繼續(xù)往下看济竹,我自己寫的都累了,需要進一步訪問login_loop這個url
login_index = session.get(loop_url, headers=headers)
uuid = login_index.text
print(login_index.status_code) #測試登錄是否成功
session.cookies.save() #保存cookies
# ↑↑↑↑↑↑到上面其實已經(jīng)完成了登錄了
response = session.get('http://weibo.com/',headers=headers)
# print(response.text) #測試用霎槐,可以打印出來看看是否與正常登錄看到的微博首頁的源碼一樣了
# 以下是原文作者用來登錄微博個人首頁獲得使用者微博賬號送浊,并打印出來的代碼,我沒有詳細解析
uuid_pa = r'"uniqueid":"(.*?)"'
uuid_res = re.findall(uuid_pa, uuid)[0]
web_weibo_url = "http://weibo.com/%s/profile?topnav=1&wvr=6&is_all=1" % uuid_res
weibo_page = session.get(web_weibo_url, headers=headers)
weibo_pa = r'<title>(.*?)</title>'
# print(weibo_page.content.decode("utf-8"))
userID = re.findall(weibo_pa, weibo_page.content.decode("utf-8", 'ignore'), re.S)[0] #不加re.S參數(shù)好像沒什么不同丘跌,上文我沒加
print(u"歡迎你 %s, 你在正在使用 xcaojianhong 寫的爬蟲模擬登錄微博" % userID)
print(input(u'輸入任何鍵繼續(xù)'))
print(u'好吧其實大部分代碼都是參考xchaoinfo的文章')
if __name__ == "__main__":
username = input(u'用戶名:')
password = input(u'密碼:')
login(username, password)
測試結(jié)果: