效果展示:

编辑
功能介绍:输入好友昵称,获取在QQ空间中,谁给TA点赞最多和TA给谁点赞最多的数据
目录
一,获取登录信息
二, 进行数据爬取
1.寻找要爬取的点赞信息
2.定位每一条说说
3.获取所有QQ好友信息
4.Threadpool多线程处理
5.参数获得与文件处理
6.第二部分代码
三,数据分析
四,UI展示——PyQt5
五,代码过程与遇到的问题
一,获取登录信息
既然决定了要对QQ空间进行信息爬取,那么作为一个需要账号密码进入的私人网页,首先需要的就是请求里的cookie信息,这样我们在后续爬取的过程中才能跳过登录,直接获取信息。
原本想使用Chrome handless的方式无窗口登录,但传统的账号密码登录需要手机验证码,尝试扫描二维码的方式却又发现使用urlretrive下载的二维码图片会被刷新更新而不可使用。
最后选择了最基础的selenium方式,打开浏览器界面,只需要简单登录,等待5s后页面会自动关闭,而后也就获取到了cookie信息,简单明了而又方便。

编辑
这里使用的代码是第一个程序文件:cookie_load.py
二, 进行数据爬取
1.寻找要爬取的点赞信息
首先我们进入空间,点击说说,进入说说模块 
编辑
而后找到其中一条说说,点击“等45人”处,会弹出点赞的好友列表
观察右侧Network响应,会发现有一个get_like_list_app,通过preview可以看出其中包含了所有点赞好友的信息,可以说是很详细了,昵称,QQ号,性别,星座,地区都有。
这时我们可以确定,它就是我们要爬取的信息辣

编辑
此时就要找每一条说说的get_like_list的url之间的规律,观察相邻几条说说的get_like_list的url,可以发现,只有参数tid是不同的,每一条说说,对应唯一的tid

编辑
之后我们的任务就是寻找每一个说说所对应的tid,也就是定位每一个说说的信息了,让我们回到说说主页,刷新界面,继续寻找包含了整页说说信息的端口
实现这部分功能爬取的代码如下,其中的参数m是下一个部分获得的说说信息
2.定位每一条说说
回到说说主页,刷新界面,观察右侧端口,发现其中emotion_cgi_msglist_v6
点开发现,它包含了一整页说说的详细信息

编辑
通过爬取它的内容,我们就可以定位到当页的每一条说说
而后和上一步一样,观察每一页说说的url,寻找规律,发现不同页码的 emotion_cgi_msglist_v6的url,只有参数pos发生了变化,第一页的pos=0,第二页的pos=20,以此类推。
这样一来,我们就能够通过拼接url来批量爬取所有页码的说说了
通过函数locate来实现这个功能,代码如下:
3.获取所有QQ好友信息
我们希望能够得到一个比较大的数据量——即爬取所有好友的说说信息,而后展开数据分析。
因此在上述步骤我们已经拥有爬取一个好友的所有说说信息之后,我们需要获得所有好友的信息(昵称+QQ号),从而循环爬取所有好友的信息。
获得所有好友信息的端口仍然从QQ空间找到。
点开我的说说,点击“@”按键,系统将弹出所有的好友供你选择@谁,同时观察右侧的network端口,出现了friend_show_qqfriends.cgi,一个从名称上看起来就很像我们要寻找的端口,进入发现,果然包含了所有好友的信息。

编辑
之后仍然和之前的步骤一样,不过这次省去了寻找url规律的过程,通过requests方法获得数据并处理,就得到了我们所有的好友数据。
这部分的功能通过函数get_friend实现,代码如下:
4.Threadpool多线程处理
在完成了上述的工作后,我们已经具备了爬取所有好友说说点赞信息的能力。但实际操作起来爬取一个好友的一页说说就需要2~3秒的时间,爬取所有好友的所有说说时间不可估计。
因此我们需要提高爬虫的速度,采用threadpool多线程处理的方法,有一个固定的模板,因此操作起来相对简单,套用即可,使用后效果甚佳,速度大大提升。
通过函数start实现,代码如下:
5.参数获得与文件处理
这里回顾一下第二部分数据爬取的两个细节问题。
参数获得,在进行url的parse.urlencode拼接时,需要两个关键的参数:uin和gtk,其中uin就是自己的qq号码,通过cookie信息可以得到,gtk是一个重要的参数,几乎所有url拼接中都要用到它,它的获得方式我们在网上可以查到,是通过函数计算而得 的。
通过get_uin和get_gtk两个函数可以获得两个参数,代码如下:
第二部分获得的数据如何为之后的数据分析所用呢,我们这里采取的是存为一个like.txt的方式,供后续读取。
6.第二部分代码
第二部分整体由文件data_collect.py实现
代码如下:
import urllib
import requests
import json
import re
import threadpool
import urllib.parse
import time
import operator as op
import csv
class Qzone:
def get_uin(self):
uin = cookie['uin'][1:]
return uin
def get_gtk(self):
p_skey = cookie['p_skey']
h = 5381
for i in p_skey:
h += (h << 5) + ord(i)
g_tk = h & 2147483647
return g_tk
def get_friend(self):
at_url = 'https://user.qzone.qq.com/proxy/domain/r.qzone.qq.com/cgi-bin/tfriend/friend_show_qqfriends.cgi?'
g_tk = self.get_gtk()
uin = self.get_uin()
data = {
'uin': uin,
'do': 1,
'g_tk': g_tk
}
new_data = urllib.parse.urlencode(data)
at_url = at_url + new_data
res = requests.get(at_url, headers=header, cookies=cookie)
friend_json = re.findall('\((.*)\)', res.text, re.S)[0]
with open('friend_list.json', 'w', encoding='utf-8') as fp:
fp.write(friend_json)
friend_dict = json.loads(friend_json)
friend_result_list = []
for friend in friend_dict['items']:
friend_result_list.append([friend['name'], friend['remark'], friend['uin'], friend['img']])
return friend_result_list
def refine(self):
r_list = []
for friend in self.get_friend():
r_list.append([friend[2], friend[0]])
return r_list
def uinlist(self):
uin_list = []
for friend in self.get_friend():
uin_list.append(friend[2])
return uin_list
'''
通过此函数对每一个人的QQ空间进行爬取
先爬取说说每一页整页数据,通过对比发现,一页有二十个说说,不同页的说说url受参数pos控制
然后根据每一页的信息对每一个说说进行定位
'''
def locate(self, qq, name):
g_tk = self.get_gtk()
uin = self.get_uin()
page = 1
pos = 0
while True:
emotion_url = 'https://user.qzone.qq.com/proxy/domain/taotao.qq.com/cgi-bin/emotion_cgi_msglist_v6?'
emotion_data = {
'uin': qq,
'pos': pos,
'num': 20,
'hostUin': qq,
'replynum': 100,
'callback': '_preloadCallback',
'code_version': 1,
'format': 'jsonp',
'need_private_comment': 1,
'g_tk': g_tk,
}
emotion_url = emotion_url + urllib.parse.urlencode(emotion_data)
res = requests.get(emotion_url, headers=header, cookies=cookie)
r = re.findall('\((.*)\)', res.text)[0]
with open(f'find/testfind{name}_{page}.json', 'w', encoding='utf-8') as fp:
fp.write(r)
message = json.loads(r)
print(f'成功读取{name}的第{page}页说说')
pos += 20
page += 1
if 'msglist' not in message:
break
if message['msglist'] == None:
print(f'{name}的空间爬取结束')
break
for m in message['msglist']:
self.get_like(m, qq, name)
def get_like(self, m, qq, name):
g_tk = self.get_gtk()
uin = self.get_uin()
tid = m['tid']
url_like = 'https://user.qzone.qq.com/proxy/domain/users.qzone.qq.com/cgi-bin/likes/get_like_list_app?'
data_like = {
'uin': uin,
'unikey': 'http://user.qzone.qq.com/' + str(qq) + '/mood/' + str(tid) + '.1',
'begin_uin': 0,
'query_count': 60,
'if_first_page': 1,
'g_tk': g_tk
}
data_like_encode = urllib.parse.urlencode(data_like)
url_like = url_like + data_like_encode
res = requests.get(url_like, headers=header, cookies=cookie)
res.encoding = 'UTF-8'
like_data = re.findall('\((.*)\)', res.text, re.S)[0]
like_message = json.loads(like_data)
try:
for l in like_message['data']['like_uin_info']:
file_like.write(name + '<-' + str(l['nick']) + '\n')
except KeyError:
print(name)
print(like_message)
def get(self, f_list):
self.locate(f_list[0], f_list[1])
def start(self):
r_list = self.refine()
pool = threadpool.ThreadPool(10)
requests = threadpool.makeRequests(self.get, r_list)
for req in requests:
pool.putRequest(req)
pool.wait()
def trans_name(self):
friend_list = self.get_friend()
for f in friend_list:
if f[1] != '':
file_trans.write(f[0] + '<-' + f[1] + '\n')
else:
file_trans.write(f[0] + '<-' + f[0] + '\n')
if __name__ == '__main__':
qzone = Qzone()
header = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
"accept-language": "zh-CN,zh;q=0.9",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
}
with open('cookie_dict.txt', 'r') as f:
cookie = json.load(f)
qzone.get_friend()
uin_list = qzone.uinlist()
file_like = open('like.txt', 'w', encoding='UTF-8')
file_trans = open('trans.txt', 'w', encoding='UTF-8')
qzone.start()
qzone.trans_name()

三,数据分析
通过第二部分得到的like.txt文本,数据分析部分首先通过get_content函数,获得文本中的内容;而后通过like_analyze和like_ananlyze2两个函数,分别获得谁给TA点赞最多和TA给谁点赞最多的数据,以字典形式返回。
注意的是:TA给谁点赞最多的数据往往不够16人,这里需要来补齐,不然后续Qt展示的时候会出bug
这部分功能由data_analysis.py实现,这个文件不需要运行,仅提供函数。
代码如下:
四,UI展示——PyQt5
受同学启发,尝试了用PyQt5做一个简单的页面。
相比于以前用的easyx复杂繁琐的经历,PyQt5简单明了,效果甚好。
首先用Qt Designer设计出自己想要的界面,并设置好接口和槽,这里的槽随便选一个,后续还要在代码中对槽根据自己的需求引入函数。
将其保存,得到like_analysis.ui

编辑
之后将like_analysis.ui通过PyUIC转换为like_analysis.py文件,代码如下:
而后通过固定的模板设计方法,写出展示的文件,这里命名为show_page.py
代码如下:
运行这个文件,就可以得到最终效果

编辑
五,代码过程与遇到的问题
首先就是登录问题,本来想做一个扫描二维码的登录方式,先是对二维码url通过正则表达式寻找定位花了很长时间,而后又发现urlretrive下载下来的二维码是过期的二维码,看网上类似教程,更改方式获取图片后仍然无效。这个过程耗费了大量时间,最后改成了最基础的selenium方法登录,效果出奇的好。
之后是寻找需要数据的url,QQ空间真是奇特,在点赞数据的url寻找上还算顺利,就是会又说说出现如下界面

编辑
没有什么规律,,,数据就获取不到了,很头疼。
而后是整体获得整页说说的过程,很重要的接口是emotion_cgi_msglist_v6,但在一个好友的空间中苦苦翻寻却怎么也找不到,换了其它好友却一下子就找到了,中途让我一度想放弃去改道交以前爬微信公众号的程序...
一切完事具备,跑起来也可以,好耶,总该ok了吧,然后一天夜里我调试程序的时候,发现运行过后like.txt中空空如也,心急如焚的我确认代码没有改过,打开QQ空间一看:
哦,我访问不了任何人的QQ说说了

编辑

编辑
腾讯我... ...
好在第二天又可以了,不知道是腾讯给我解封了还是我真的把网址跑崩了... ...
好在不同于以往爬取网站信息单纯就是寻找正则表达式,模拟浏览器操作selenium就是找借口,此次项目从寻找参数,观察url,python文件使用读取,等多方面都有练习,同时意识到了自己基础字典列表使用上不熟带来的各种bug...还学到了threadpool和PyQt5新技巧。
更重要的是,“喂喂喂,你想不想知道谁给你点赞最多,和你给谁点赞最多呢”
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现