一键生成属于自己的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格式

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:766545907
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
    # 初始化文件夹
    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'])

并获取所有群数据

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:766545907
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
    # 获取所有群信息
    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登录为例:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:766545907
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
    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模块

这个模块主要是用来存储相关的数据的,在程序每次运行时,将该静态资源文件输出。这么做的原因是可以防止用户将某些静态数据给删除了,导致程序运行错误。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容