新版知乎模拟登陆
1.Cookie和Session的區(qū)別
如果你登錄知乎,填寫過用戶名、密碼下次進(jìn)來(lái)的時(shí)候不想再填寫了,那么你在第一次登錄后,服務(wù)器就會(huì)發(fā)送給你的瀏覽器一個(gè)Cookie,Cookie中包含了你的用戶名、密碼,下次再次發(fā)送請(qǐng)求給知乎的時(shí)候,瀏覽器會(huì)自動(dòng)給請(qǐng)求加上Cookie,這樣服務(wù)器就能知道你是誰(shuí)。這就是Cookie的機(jī)制。
但是這種機(jī)制是不安全的,當(dāng)你本地Cookie被別人獲取后,就能直接使用你的賬號(hào)了。于是出現(xiàn)了session機(jī)制:當(dāng)你第一次以用戶名、密碼登錄知乎時(shí),知乎服務(wù)器會(huì)自己生成一條Session Id和sesson Value保存在服務(wù)器端,同時(shí)將Session Id作為Cookie中的一個(gè)鍵值對(duì)返回給瀏覽器,當(dāng)你第二次請(qǐng)求知乎服務(wù)器的時(shí)候,請(qǐng)求會(huì)自動(dòng)加上Session Id,那么服務(wù)器端收到Session Id后,會(huì)在服務(wù)器上查詢是否有此Session Id,如果查詢到,那么就匹配到相應(yīng)的Session Value,也就是包含用戶名密碼的部分,這時(shí)候服務(wù)器就能識(shí)別出來(lái)你是那個(gè)用戶了。
明確一點(diǎn):Session Value中包含的是加密的用戶名、密碼等用戶的Profile,是存放在服務(wù)器端,同事每一個(gè)Session Id都是有一個(gè)有效期的,這兩點(diǎn)保證了安全性。
2.Http狀態(tài)碼
| 200 | 請(qǐng)求被成功處理 |
| 301/302 | 永久重定向/臨時(shí)重定向 |
| 403 | 沒有訪問權(quán)限 |
| 404 | 滅有對(duì)應(yīng)的資源 |
| 500 | 服務(wù)器錯(cuò)誤 |
| 503 | 服務(wù)器停機(jī)或正在維護(hù) |
3.分析知乎登錄
3.1 找到登錄請(qǐng)求URL
通過嘗試手機(jī)號(hào)碼和郵箱號(hào),分析得出知乎統(tǒng)一登錄網(wǎng)址為:https://www.zhihu.com/api/v3/oauth/sign_in
3.2 找到登錄時(shí)的header部分
:authority: www.zhihu.com :method: POST origin: https://www.zhihu.com referer: https://www.zhihu.com/signup?next=%2F user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36 x-xsrftoken: 405b7e07-d4f5-4b35-8b70-3e129d97a4d8主要的是找到x-xsrftoken,以前的教程寫的都是_xsrf在返回的html中標(biāo)簽中,改版后變化了,我們可以在返回的cookie中找到,所以
首先設(shè)置一個(gè)header
取得_xsrf
def get_xsrf():response = session.post("https://www.zhihu.com/signup?next=%2F", headers=header) return response.cookies['_xsrf']隨后更新header
header.update({"X-Xsrftoken": xsrf})3.3分析請(qǐng)求登錄頁(yè)面的data數(shù)據(jù)
3.3.1得到請(qǐng)求的data數(shù)據(jù)格式為下
------WebKitFormBoundary4YUIdOtklYTeomJn Content-Disposition: form-data; name="client_id"c3cef7c66a1843f8b3a9e6a1e3160e20 ------WebKitFormBoundary4YUIdOtklYTeomJn Content-Disposition: form-data; name="grant_type"password ------WebKitFormBoundary4YUIdOtklYTeomJn Content-Disposition: form-data; name="timestamp"1527727860453 ------WebKitFormBoundary4YUIdOtklYTeomJn Content-Disposition: form-data; name="source"com.zhihu.web ------WebKitFormBoundary4YUIdOtklYTeomJn Content-Disposition: form-data; name="signature"8417b29b51739a7dba377934e7678c35625464f0 ------WebKitFormBoundary4YUIdOtklYTeomJn Content-Disposition: form-data; name="username"+8615639151994 ------WebKitFormBoundary4YUIdOtklYTeomJn Content-Disposition: form-data; name="password"admin123 ------WebKitFormBoundary4YUIdOtklYTeomJn Content-Disposition: form-data; name="captcha"------WebKitFormBoundary4YUIdOtklYTeomJn Content-Disposition: form-data; name="lang"en ------WebKitFormBoundary4YUIdOtklYTeomJn Content-Disposition: form-data; name="ref_source"homepage ------WebKitFormBoundary4YUIdOtklYTeomJn Content-Disposition: form-data; name="utm_source"------WebKitFormBoundary4YUIdOtklYTeomJn--3.3.2 通過多次請(qǐng)求發(fā)現(xiàn)如下會(huì)改變的信息只有
username: password: timestamp: captcha: signature:其中用戶名、密碼是自己控制的
3.3.2.1 timestamp
比較簡(jiǎn)單就是距離1970年過去的秒數(shù)的整數(shù)部分。
timestamp = str(int(time.time()))3.2.2.2 captcha
驗(yàn)證碼處理:現(xiàn)在知乎更新了,有的使用字母驗(yàn)證碼,有的使用點(diǎn)擊倒立的數(shù)字驗(yàn)證碼。但是我發(fā)現(xiàn)登錄時(shí)只考慮圖形字母驗(yàn)證碼也可以登錄,但不知道* 為什么 *?
對(duì)字母驗(yàn)證碼處理的方法就是下載圖片,然后打開,進(jìn)行手動(dòng)輸入
分析對(duì)驗(yàn)證碼圖片的請(qǐng)求url和header
請(qǐng)求url為:https://www.zhihu.com/api/v3/oauth/captcha?lang=cn
header如下:
:authority: www.zhihu.com:method: GET:path: /api/v3/oauth/captcha?lang=cn:scheme: httpsaccept: application/json, text/plain, */*accept-encoding: gzip, deflate, braccept-language: zh-CN,zh;q=0.9,en;q=0.8authorization: oauth c3cef7c66a1843f8b3a9e6a1e3160e20cookie: d_c0="AIDk5lsxrA2PTn6UE7w8ZXwIcLwr6s4V8TM=|1527673963"; q_c1=29e9198d965c42b4b4d13820bc7023db|1527673963000|1527673963000; _zap=a0bc8c13-50e7-484b-abde-db97010a065b; l_cap_id="YzIxYmFmNzg0YjViNGZmODljNTIwMjUwMTQ5NWY0NTY=|1527688563|f3bfccc25e61ab0e6c92295650f257e2f9cd779b"; r_cap_id="YTE3M2JlMWQ4NWQzNDA2NzllYThmMWYxNjMxZmRhMTY=|1527688563|be9b0cd7843bf090e5e0194ceb29a99fc60a1cce"; cap_id="ZWFjYTg3ODljMzI0NDVmOTgyYmE0NjRiMGQyZGRmYmU=|1527688563|4636e8decd51156afe879407f16e6c7f57e222ce"; tgw_l7_route=5bcc9ffea0388b69e77c21c0b42555fe; _xsrf=405b7e07-d4f5-4b35-8b70-3e129d97a4d8; capsion_ticket="2|1:0|10:1527727674|14:capsion_ticket|44:Y2M3YTcwYWQ3OGFiNGIxMjk3MWUwY2I5ZWQ0OWM0ZjQ=|ed0700eadd8466ffd6f4c61dfbce11a1f4d11483f32f1ae8262501a7fd859558"if-none-match: "fa4cf03c0ac47ca1c52ed2df2b71dfda86db6655"referer: https://www.zhihu.com/signup?next=%2Fuser-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36x-udid: AIDk5lsxrA2PTn6UE7w8ZXwIcLwr6s4V8TM=發(fā)現(xiàn)了有一個(gè)authorization字段,我在此踩坑,沒加這個(gè)字段的header不能成功請(qǐng)求圖片驗(yàn)證碼
所以:
3.2.2.3 signature
這邊是參照大神的分析
多次請(qǐng)求,其他參數(shù)都是固定的,但是signature參數(shù)是個(gè)什么東西…知道意思是簽名,但是我們要從哪里獲取這個(gè)呢.網(wǎng)頁(yè)源碼里沒有,那肯定是js生成的,去js里搜索在
https://static.zhihu.com/heifetz/main.app.19b9c7c4c4502d8ef477.js 中總算是找到了.為了好看下載到編譯器里,實(shí)在太大,編譯器直接卡死了,太尷尬了….漫長(zhǎng)的等待后拿到這么一段js
可以看出是使用sha-1 key=’d1b964811afb40118a12068ff74a12f4’和其他的一些字段生成的HMAX
def get_signature(time_str):h = hmac.new(key='d1b964811afb40118a12068ff74a12f4'.encode('utf-8'), digestmod=sha1)grant_type = 'password'client_id = 'c3cef7c66a1843f8b3a9e6a1e3160e20'source = 'com.zhihu.web'now = time_strh.update((grant_type + client_id + source + now).encode('utf-8'))return h.hexdigest()3.4 封裝data和header進(jìn)行登錄
def zhihu_login(account, password):# 知乎登錄time_str = str(int(time.time()))xsrf = get_xsrf()header.update({"X-Xsrftoken": xsrf})post_url = "https://www.zhihu.com/api/v3/oauth/sign_in"post_data = {"client_id": "c3cef7c66a1843f8b3a9e6a1e3160e20","grant_type": "password","timestamp": time_str,"source": "com.zhihu.web","signature": get_signature(time_str),"username": account,"password": password,"captcha": get_captcha(),"lang": "cn","ref_source": "homepage","utm_source": ""}response = session.post(post_url, data=post_data, headers=header, cookies=session.cookies)if response.status_code == 201:# 保存cookie,下次直接讀取保存的cookie,不用再次登錄print("登錄成功")response = session.post("https://www.zhihu.com", headers=header, cookies=session.cookies)#print(response.text)session.cookies.save()else:print("登錄失敗")如果登錄成功我們要保存cookie,下次登錄就直接實(shí)用cookie登錄即可,所以我們首先要加載cookie
session = requests.session()session.cookies = cookielib.LWPCookieJar(filename="cookies.txt") # cookie存儲(chǔ)文件,try:session.cookies.load(ignore_discard=True) # 從文件中讀取cookie except:print("cookie 未能加載")3.5 判斷是否已經(jīng)登錄
因?yàn)槲覀兗虞d了cookie,如果cookie有效且正確,這不用再次登錄
這里采用點(diǎn)擊用戶個(gè)人中心的鏈接,且不允許跳轉(zhuǎn),從而獲取返回的狀態(tài)碼,如果狀態(tài)碼為200則證明登錄了,否則要進(jìn)行登錄
def is_login():# 通過個(gè)人中心頁(yè)面返回狀態(tài)碼來(lái)判斷是否登錄# 通過allow_redirects 設(shè)置為不獲取重定向后的頁(yè)面response = session.get("https://www.zhihu.com/inbox", headers=header, allow_redirects=False)print(response.cookies)print(response.status_code)if response.status_code != 200:print("尚未登錄")zhihu_login("+***", "***")else:print("你已經(jīng)登陸了")至此,就可以成功登錄啦!
具體實(shí)現(xiàn)完整代碼,請(qǐng)移駕github
總結(jié)
- 上一篇: 图书漂流系统的设计和研究_研究在设计系统
- 下一篇: spring mvc学习(1):spri