一鍵生成屬於自己的QQ歷史報告,看看你對自己的QQ了解程度有多深?


一鍵生成屬於自己的QQ歷史報告,看看你對自己的QQ了解程度有多深?

簡介

近幾年,由於微信的流行,大部分人不再頻繁使用QQ,所以我們對於自己的QQ數據並不是特別了解。我相信,如果能夠生成一份屬於自己的QQ歷史報告,那將是無比開心的一件事。

目前網上關於QQ的數據分析工具較少,原因是QQ相關接口比較復雜。而本程序的運行十分簡單,具有良好的用戶交互界面,只需要掃碼登錄一步操作即可。

目前本程序獲取的數據包括:QQ詳細數據、手機在線時間、非隱身狀態下在線時間、QQ活躍時間、單向好友數量、QQ財產分析、群聊分析、過去一年我退出的群聊數據、退去一個月我刪除的好友數據、所有代付信息、我最在意的人以及最在意我的人。由於相關的數據接口有訪問限制,所以本程序並沒有對QQ好友進行分析。

功能截圖




如何運行

# 跳轉到當前目錄
cd 目錄名
# 先卸載依賴庫
pip uninstall -y -r requirement.txt
# 再重新安裝依賴庫
pip install -r requirement.txt
# 開始運行
python main.py

編寫思路

本程序分為多個模塊,模塊如下:

  1. main.py,主程序,用於獲取並處理相關數據,並導出數據報告。
  2. qq_bot.py, 核心模塊,實現了qq相關的接口,較為復雜。
  3. tkinter_gui.py,繪制gui模塊,使用tkinter繪制基本的交互界面。
  4. static_data.py,數據存儲模塊,所有數據采用base64編碼存儲。

main.py模塊

首先,初始化相關文件夾,並調用qq_bot.py模塊,定義一個qq bot對象,該對象為本程序的核心對象,所有數據獲取均從該對象獲取。
同時,本程序數據的報告文件為.md格式

	# 初始化文件夾
	init_folders()
	# 寫入項目所需資源文件到本地目錄
	write_data()

	# 創建一個自己編寫的qq bot對象
	bot = Bot()
	custom_print(u'登錄成功,正在獲取數據...')

	# 定義欲輸出的markdown字符串
	markdown_content = '''
	<p align="center">
	<font size='6px'>{qq_number}的個人QQ歷史報告</font>
	<img src="{qq_icon_png}" align="right" height="60">
	</p>
	'''
	# 更新一下欲輸出的markdown文本
	markdown_content = markdown_content.replace('{qq_number}',bot.qq_number)
	markdown_content = markdown_content.replace('{qq_icon_png}', 'data/qq_icon.png')

登錄成功后,開始獲取該登錄賬戶的詳細資料

    custom_print(u'正在獲取該登錄賬戶的詳細數據...')
    detail_information = bot.get_detail_information()
    # content為markdown語法文本
    content = '\n<br/><br/>\n' + '## 我的詳細資料\n' + '種類|內容\n:- | :-\n'
        for key, value in detail_information.items():
        if key == 'qq_level':
            star_count, moon_count, sun_count, crown_count = calculate_level(value)
            data = crown_count * '![](data/level_crown.png)' + sun_count * '![](data/level_sun.png)' + moon_count * '![](data/level_moon.png)' + star_count * '![](data/level_star.png)'
            content += '{}|{}\n'.format(key_dict[key], data)
        else:
            content += '{}|{}\n'.format(key_dict[key], value)
    # 更新一下欲輸出的markdown文本
    markdown_content += content
    markdown_content += '\n> 注:單向好友表示他/她的列表中有你,而你的列表中沒有他/她'
    # 每個步驟完成后,保存markdown文件,以便防止程序出錯時能夠保存到最新的數據
    with open('{}的個人QQ歷史報告.md'.format(bot.qq_number), 'w', encoding='utf-8') as file:
        file.write(markdown_content)

接着,獲取所有qq好友的備注名和qq號

    all_qq_friends = bot.get_all_friends_in_qq()
    custom_print(u'所有qq好友號碼和備注名中...')
    qq_number_list = []
    for key, friend_group in all_qq_friends.items():
        for info in friend_group['mems']:
            qq_number_list.append(info['uin'])

並獲取所有群數據

    # 獲取所有群信息
    custom_print(u'獲取該QQ加入的所有群信息...')
    group_list = bot.get_group()
    print(group_list)
    # content為markdown語法文本
    content = '\n\n<br/><br/>\n' + '## 我加入的群資料\n' + '序號|群名|群號|群主QQ\n:- | :-| :-| :-\n'
    # 獲取某個群的群成員信息
    for index, group in enumerate(group_list):
        group_number = group['gc']
        group_name = group['gn']
        owner = group['owner']
        content += '{}|{}|{}|{}\n'.format(str(index+1), str(group_name), str(group_number), str(owner))

    # 更新一下欲輸出的markdown文本
    markdown_content += content
    # 每個步驟完成后,保存markdown文件,以便防止程序出錯時能夠保存到最新的數據
    with open('{}的個人QQ歷史報告.md'.format(bot.qq_number), 'w', encoding='utf-8') as file:
        file.write(markdown_content)

接下來的步驟如你所需,也就是獲取其他相關的數據,所以本小節就不一一詳細解釋了,您可以查看相關源代碼查看。獲取的數據包括:

  1. 獲取過去30天內退出的群名單
  2. 獲取過去364天內刪除的好友名單
  3. 判斷此次登錄的qq是否為vip或者svip
  4. 獲取qb值
  5. 獲取代付信息
  6. 親密度排行榜
  7. 共同好友數
  8. 成為好友的天數

qq_bot模塊

此模塊實現了獲取qq數據的接口,主要通過抓包獲得數據、分析數據,對參數進行加密解密等。

首先,是模擬掃碼登錄id.qq.com,qun.qq.com,qzone.qq.com。三者登錄方式大同小異,唯一有區別的就是提交數據中的參數加密方式不同。
我們以id.qq.com登錄為例:

    def login_id_qq_com(self):
        # 登錄id.qq.com

        # 訪問網頁,為了獲取參數pt_login_sig
        login_url = 'https://xui.ptlogin2.qq.com/cgi-bin/xlogin?pt_disable_pwd=1&appid=1006102&daid=1&style=23&hide_border=1&proxy_url=https://id.qq.com/login/proxy.html&s_url=https://id.qq.com/index.html'
        html = get_html(login_url, '')
        # 對返回的cookies進行轉化為dict類型,方便處理
        cookies_back_dict = dict_from_cookiejar(html.cookies)
        pt_login_sig = cookies_back_dict['pt_login_sig']
        self.cookies_merge_dict_in_id_qq_com.update(cookies_back_dict)

        # 訪問網頁,為了獲取參數ptqrtoken
        qrcode_url = 'https://ssl.ptlogin2.qq.com/ptqrshow?appid=1006102&e=2&l=M&s=4&d=72&v=4&t=0.10239549811477189&daid=1&pt_3rd_aid=0'
        html = get_html(qrcode_url, '')
        # 對返回的cookies進行轉化為dict類型,方便處理
        cookies_back_dict = dict_from_cookiejar(html.cookies)
        qrsig = cookies_back_dict['qrsig']
        ptqrtoken = hash33_token(qrsig)
        self.cookies_merge_dict_in_id_qq_com.update(cookies_back_dict)


        # 將二維碼顯示到圖片框
        BytesIOObj = BytesIO()
        BytesIOObj.write(html.content)
        qr_code = PIL.Image.open(BytesIOObj)
        image = PIL.ImageTk.PhotoImage(qr_code)
        image_label['image'] = image


        # 實時檢測二維碼狀態
        while (True):
            # 目標網址
            target_url = 'https://ssl.ptlogin2.qq.com/ptqrlogin?u1=https://id.qq.com/index.html&ptqrtoken=' + str(ptqrtoken) + '&ptredirect=1&h=1&t=1&g=1&from_ui=1&ptlang=2052&action=0-0-1556812236254&js_ver=19042519&js_type=1&login_sig=' + str(pt_login_sig) + '&pt_uistyle=40&aid=1006102&daid=1&'

            # 登錄,需要帶上訪問cookies
            html = get_html(target_url, self.cookies_merge_dict_in_id_qq_com)

            # 返回的響應碼為200說明二維碼沒過期
            if (html.status_code):
                if ('二維碼未失效' in html.text):
                    custom_print(u'(1/3)登錄id.qq.com中,當前二維碼未失效,請你掃描二維碼進行登錄')
                elif ('二維碼認證' in html.text):
                    custom_print(u'(1/3)登錄id.qq.com中,掃描成功,正在認證中')
                elif ('登錄成功' in html.text):
                    self.is_login = True
                    custom_print(u'(1/3)登錄id.qq.com中,登錄成功')
                    break
                if ('二維碼已經失效' in html.text):
                    custom_print(u'(1/3)登錄id.qq.com中,當前二維碼已失效,請重啟本軟件')
                    exit()

            # 延時
            time.sleep(2)

        # 登錄成功后,把返回的cookies合並進去
        self.cookies_merge_dict_in_id_qq_com = dict_from_cookiejar(html.cookies)
        self.cookies_merge_dict_in_id_qq_com.update(cookies_back_dict)
        # print(u'當前cookies:{}'.format(cookies_merge_dict))

        # 獲取此次登錄的qq號碼
        qq_list = re.findall(r'&uin=(.+?)&service', html.text)
        self.qq_number = qq_list[0]


        # 登錄成功后,會返回一個地址,需要對該地址進行訪問以便獲取新的返回cookies
        startIndex = (html.text).find('http')
        endIndex = (html.text).find('pt_3rd_aid=0')
        url = (html.text)[startIndex:endIndex] + 'pt_3rd_aid=0'

        # 屏蔽https證書警告
        urllib3.disable_warnings()

        # 這里需要注意的是,需要禁止重定向,才能正確獲得返回的cookies
        html = get(url, cookies=self.cookies_merge_dict_in_id_qq_com, allow_redirects=False, verify=False)
        # 把返回的cookies合並進去
        cookies_back_dict = dict_from_cookiejar(html.cookies)
        self.cookies_merge_dict_in_id_qq_com.update(cookies_back_dict)

首先是訪問指定網址,獲取參數pt_login_sig,其次是訪問另外一個網址,獲取參數qrsig,通過加密函數,將參數qrsig轉化為ptqrtoken,然后就是獲取二維碼圖片的狀態了。當我們檢測到登錄成功時,就證明用戶已經完成掃碼操作,此時將網址返回的cookie保存下來。

這里要說明的是,加密函數的獲取,需要具備一定的抓包基礎才能獲取得到。本程序的幾個加密函數如下:

# 對qrsig進行基本的加密,該加密函數由抓包獲得,需要具備一定抓包知識才能找到該加密函數
# 根據javascript版的加密函數,將其改寫成python版本
def hash33_token(t):
    e, n = 0, len(t)
    for i in range(0,n):
        e += (e << 5) + ord(t[i])
    return 2147483647 & e



# 對skey進行基本的加密,該加密函數由抓包獲得,需要具備一定抓包知識才能找到該加密函數
# 根據javascript版的加密函數,將其改寫成python版本
def hash33_bkn(skey):
    e = skey
    t = 5381

    for n in range(0,len(e)):
        t += (t << 5) + ord(e[n])
    return 2147483647 & t

由於該模塊下具有許多獲取相關數據的qq接口,但是它們的形式非常相似,所以本節僅僅以獲取所有qq群數據為例:

    def get_group(self):

        # 獲取所有群基本信息
        # bkn由參數skey通過另一個加密函數得到
        bkn = hash33_bkn(self.cookies_merge_dict_in_qun_qq_com['skey'])
        submit_data = {'bkn': bkn}
        html = post_html('https://qun.qq.com/cgi-bin/qun_mgr/get_group_list', self.cookies_merge_dict_in_qun_qq_com, submit_data)
        group_info = loads(html.text)
        print(group_info)
        return group_info['join']

這里主要涉及到的還是參數的加密、解密過程,這是一個難點,其他的話還是比較簡單的。

tkinter_gui模塊

這個模塊是繪制基本的gui模塊,采用python內置的tkinter模塊完成,用法相當簡單,這里就不詳細講了。

static_data模塊

這個模塊主要是用來存儲相關的數據的,在程序每次運行時,將該靜態資源文件輸出。這么做的原因是可以防止用戶將某些靜態數據給刪除了,導致程序運行錯誤。

補充

完整版源代碼存放在github上,有需要的可以下載

項目持續更新,歡迎您star本項目

License

The MIT License (MIT)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM