最新python模擬登錄知乎

步驟:

  • 第一步:抓包查看登陸接口
  • 第二步:分析js文件,提取加密請求參數的js腳本
  • 第三步:分析js文件,構造需要加密的字符串
  • 第四步:編寫代碼模擬請求登陸

第一步:抓包查看post登陸接口

登陸接口

參數(可以看到post的數據是經過加密的)

第二步:分析js文件,提取加密請求參數的js腳本

通過全局搜索sign_in字段,找到對應post請求

我們看到feachOpiton中的zsEncrypt參數為ture,zsEncrypt為ture應該是代表body需要加密,斷點后通過控制臺執行Object(d.decamelizeKeys)(e),可以獲取到body參數

通過搜索zsEncrypt字段,可以找到請求時對body進行加密的函數

斷點后發現o.default 為實際的加密函數,a為需要加密的字符串

控制臺調用o.default(a)可以獲取到加密后字符

進入o.defaul函數,o.defaul實際為加密模塊中對外暴露的Q加密函數

接下來需要提取加密模塊,然后使用python執行提取出來的js腳本

將Q函數所在的函數復制到本地zhihu.js文件,將最外層函數刪掉,只保存里面的代碼

同時將export模塊相關的代碼刪掉

在zhihu.js最后調用Q函數加密需要加密的a字符串

創建zhihu.html文件,引入zhihu.js文件

使用瀏覽器打開文件,控制臺console中可以看到加密過的字符串

此時我們已經提取出了加密所需要的js文件,可以通過瀏覽器執行,但時通過python執行會發現無法執行,原因是瀏覽器環境和python執行js的node環境不同,使用vscode斷點試調后發現node環境先缺少window對象、navigator對象(過程比較繁瑣,有興趣的可以自己去試調研究下),并且window對象下需要有encodeURIComponent函數,navigator對象下需要有userAgent屬性。同時也需要用到atob函數,通過全局搜索atob可以找到zap.js文件中的atob函數,atob函數實際上是將base64編碼的字符串轉換二進制編碼的字符串。缺少的這些我們可以自己定義出來,添加到zhihu.js文件開頭。


此時提取出來的zhihu.js文件已經可以正常運行了

我們自己定義一個encrypt函數供python調用,這樣就完成了加密js的提取

使用python調用執行js腳本

import execjs

def encrypt(string):
    with open('./zhihu.js', 'r', encoding='utf-8') as f:
        js = f.read()
    result = execjs.compile(js).call('encrypt', string)
    return result

print(encrypt('123456'))

第三步:分析js文件,構造需要加密的字符串

需要加密的字符串:

client_id=c3cef7c66a1843f8b3a9e6a1e3160e20&
grant_type=password&
timestamp=1551062570616&
source=com.zhihu.web&
signature=e3ab73425750a4dbcf9ab357f6030fc281ceeb22&
username=819221111@qq.com&
password=123456&
captcha=&
lang=en&
ref_source=homepage&
utm_source=

這些參數中會變的參數的總共有四個,分別是timestamp,signature,username,password,這些需要我們自己傳入,capthca參數是有驗證碼的時候需要傳,我還沒遇到過需要輸入驗證碼的,這里我們不作考慮。真正需要我們構造的只有signature參數,下面介紹如何構造

全局搜索signature,在main.app.xxx.js 中可以找到singnature的構造方法,是通過hmac加密
clientId,grantType,source,timestamp四個參數獲得的

使用python模擬加密的js,代碼如下

import time
import hmac
from hashlib import sha1    

def get_signature():
    h = hmac.new(key='d1b964811afb40118a12068ff74a12f4'.encode('utf-8'), digestmod=sha1)
    grant_type = 'password'
    client_id = 'c3cef7c66a1843f8b3a9e6a1e3160e20'
    source = 'com.zhihu.web'
    now = str(int(time.time()*1000))
    h.update((grant_type + client_id + source + now).encode('utf-8'))
    return h.hexdigest()
print(get_signature())

第四步:編寫代碼模擬請求登陸

我們的準備工作已經完成了,下面開始編寫代碼模擬請求

  • 第一步:請求請求login_url,udid_url,captcha_url加載所需要的cookie
  • 第二步:構造需要加密的字符串
  • 第三步:加密字符串
  • 第四步:使用加密后的字符串請求post登陸接口

完整代碼如下:

import requests
import re
import execjs
import time
import hmac
from hashlib import sha1

class Zhihu(object):

    def __init__(self, username, password):

        self.username = username
        self.password = password
        self.session = requests.session()
        # 此處請求頭只需要這三個
        self.headers = {
            'content-type': 'application/x-www-form-urlencoded',
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36',
            'x-zse-83': '3_1.1'
        }

    def login(self):

        # 請求login_url,udid_url,captcha_url加載所需要的cookie
        login_url = 'https://www.zhihu.com/signup?next=/'
        resp = self.session.get(login_url, headers=self.headers)        
        print("請求{},響應狀態碼:{}".format(login_url,resp.status_code)) 
        # print(self.session.cookies.get_dict())
        # self.save_file('login',resp.text)

        udid_url = 'https://www.zhihu.com/udid'
        resp = self.session.post(udid_url, headers=self.headers)
        print("請求{},響應狀態碼:{}".format(udid_url,resp.status_code)) 
        # print(self.session.cookies.get_dict())

        captcha_url = 'https://www.zhihu.com/api/v3/oauth/captcha?lang=en'
        resp = self.session.get(captcha_url, headers=self.headers)
        print("請求{},響應狀態碼:{}".format(captcha_url,resp.status_code)) 
        # print(self.session.cookies.get_dict())
        # print(resp.text)
        # self.save_file('captcha',resp.text)
        
        # 校驗是否需要驗證嗎,需要則直接退出,還沒遇到過需要驗證碼的
        if re.search('true',resp.text):
            print('需要驗證碼')
            exit()
        
        # 獲取signature參數
        self.time_str = str(int(time.time()*1000))
        signature = self.get_signature()
        # print(signature)

        # 拼接需要加密的字符串
        string = "client_id=c3cef7c66a1843f8b3a9e6a1e3160e20&grant_type=password&timestamp={}&source=com.zhihu.web&signature={}&username={}&password={}&captcha=&lang=en&ref_source=homepage&utm_source=".format(self.time_str,signature,self.username,self.password)
        # print(string)
        # 加密字符串
        encrypt_string = self.encrypt(string)
        # print(encrypt_string)

        # post請求登陸接口
        post_url = "https://www.zhihu.com/api/v3/oauth/sign_in"
        resp = self.session.post(post_url, data=encrypt_string, headers=self.headers)
        print("請求{},響應狀態碼:{}".format(post_url,resp.status_code)) 
        # print(self.session.cookies.get_dict())
        # print(resp.text)
        # self.save_file('post',resp.text)

        # 校驗是否登陸成功
        if re.search('user_id',resp.text):
            print('登陸成功')
        else:
            print("登陸失敗")
            exit()

    def test(self):

        # 請求個人信息接口查看個人信息
        me_url = 'https://www.zhihu.com/api/v4/me'
        data = {
            'include': 'ad_type;available_message_types,default_notifications_count,follow_notifications_count,vote_thank_notifications_count,messages_count;draft_count;following_question_count;account_status,is_bind_phone,is_force_renamed,email,renamed_fullname;ad_type'
        }
        resp = self.session.get(me_url, data=data, headers=self.headers)
        print("請求{},響應狀態碼:{}".format(me_url,resp.status_code)) 
        print(resp.text)
        # self.save_file('me',resp.text)

    def encrypt(self, string):

        with open('./zhihu.js', 'r', encoding='utf-8') as f:
            js = f.read()
        result = execjs.compile(js).call('encrypt', string)
        return result

    def get_signature(self):

        h = hmac.new(key='d1b964811afb40118a12068ff74a12f4'.encode('utf-8'), digestmod=sha1)
        grant_type = 'password'
        client_id = 'c3cef7c66a1843f8b3a9e6a1e3160e20'
        source = 'com.zhihu.web'
        now = self.time_str
        h.update((grant_type + client_id + source + now).encode('utf-8'))
        return h.hexdigest()

    def save_file(self, name, html):

        with open('{}.html'.format(name),'w',encoding='utf-8') as f:
            f.write(html)
        

if __name__ == "__main__":

    account = Zhihu('賬號','密碼')
    account.login()
    account.test()

登陸成功

代碼

鏈接:https://pan.baidu.com/s/1aqzzkacgQM0n2ewV6r7csg
提取碼:inv2

參考文章

https://zhuanlan.zhihu.com/p/34073256
https://mp.weixin.qq.com/s/XplpQ6QUophvgfyMszk0Hg

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容