目录
背景说明
使用效果
参考代码
扫码登录(备份)
背景说明
Q群验证就是为了验证某个用户是否加入了指定的群聊。这可以有很多作用,比如限制软件的使用人群,以防滥用。
使用效果
参考代码
from PyQt5.QtCore import QUrl, Qt | |
from PyQt5.QtWebEngineWidgets import QWebEngineView | |
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QMessageBox, QHBoxLayout, QLabel | |
import sys | |
class QQGroupLogin(QWidget): | |
def __init__(self, groupid="913182235"): | |
super().__init__() # 调用父类的构造函数 | |
self.init_ui() # 初始化用户界面 | |
self.groupid = groupid # 指定的QQ群ID | |
def init_ui(self): | |
self.setWindowTitle('QQ群验证登录 by 小锋学长生活大爆炸') # 设置窗口标题 | |
layout = QVBoxLayout() # 创建一个垂直布局 | |
self.loading_label = QLabel('页面加载中,请稍候...') | |
layout.addWidget(self.loading_label) # 添加加载提示到布局 | |
# 创建并配置Web浏览器组件 | |
self.browser = QWebEngineView() | |
# 加载指定的URL,即QQ登录页面 | |
self.browser.load(QUrl("https://xui.ptlogin2.qq.com/cgi-bin/xlogin?pt_disable_pwd=1&appid=715030901&daid=73&hide_close_icon=1&pt_no_auth=1&s_url=https%3A%2F%2Fqun.qq.com%2Fmember.html%23")) | |
self.browser.setMinimumSize(600, 400) # 设置浏览器组件的最小大小 | |
layout.addWidget(self.browser) # 将浏览器组件添加到布局中 | |
# 连接页面加载信号 | |
self.browser.loadStarted.connect(self.on_load_started) | |
self.browser.loadFinished.connect(self.on_load_finished) | |
# 创建一个水平布局来放置按钮 | |
button_layout = QHBoxLayout() | |
# 创建“刷新页面”按钮,并绑定点击事件到浏览器的reload方法以刷新页面 | |
self.refresh_button = QPushButton('刷新页面') | |
self.refresh_button.setStyleSheet("font-size: 16px; height: 36px;") | |
self.refresh_button.clicked.connect(self.browser.reload) | |
button_layout.addWidget(self.refresh_button) # 将按钮添加到按钮布局中 | |
# 创建“检查验证”按钮,用于触发验证过程 | |
self.check_button = QPushButton('检查验证') | |
self.check_button.setStyleSheet("font-size: 16px; height: 36px;") | |
self.check_button.clicked.connect(self.check_verification) | |
button_layout.addWidget(self.check_button) # 将按钮添加到按钮布局中 | |
layout.addLayout(button_layout) # 将按钮布局添加到主布局中 | |
self.setLayout(layout) # 设置窗口的布局为之前创建的布局 | |
self.resize(800, 600) # 设置窗口的初始大小 | |
self.center_on_screen() # 将窗口居中显示 | |
self.show() # 显示窗口 | |
def center_on_screen(self): | |
# 计算并应用窗口居中的位置,确保使用整数进行move调用 | |
resolution = self.screen().availableGeometry() | |
self.move(int((resolution.width() / 2) - (self.frameSize().width() / 2)), | |
int((resolution.height() / 2) - (self.frameSize().height() / 2))) | |
def on_load_started(self): | |
self.loading_label.setVisible(True) # 显示加载提示 | |
def on_load_finished(self, ok): | |
self.loading_label.setVisible(False) # 隐藏加载提示 | |
def check_verification(self): | |
# 请求当前页面的HTML内容,用于后续处理 | |
self.browser.page().toHtml(self.process_page_source) | |
def process_page_source(self, html): | |
# 处理页面源代码,检查用户是否已加入指定的QQ群 | |
if "data-id=\"{}\"".format(self.groupid) in html: | |
QMessageBox.information(self, '验证结果', '恭喜,验证成功!') | |
else: | |
QMessageBox.warning(self, '验证结果', '您尚未加入指定的QQ群,请加群后再尝试。') | |
if __name__ == '__main__': | |
# 如果不需要调整缩放,可以注释掉下面这行 | |
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) # 新增加的,针对高DPI屏幕自动缩放 | |
app = QApplication(sys.argv) # 创建应用程序实例 | |
ex = QQGroupLogin() # 创建窗口实例 | |
sys.exit(app.exec_()) # 启动应用程序的事件循环 |
扫码登录(备份)
这段代码只作为备份,因为扫码登录能用,但Q群验证这个不行了。Q群验证还得是用上面的方法。
import requests | |
from PIL import Image, ImageTk | |
import tkinter as tk | |
import time | |
import re | |
import sys | |
from io import BytesIO # 用于将二进制内容转换为文件类对象,以便Image.open使用 | |
import base64 | |
class QQGroupLogin: | |
def __init__(self): | |
# 初始化会话以跟踪cookies和headers | |
self.session = requests.Session() | |
def compute_bkn(skey): | |
# 计算bkn,某些请求所需的参数 | |
t = 5381 | |
for character in skey: | |
t += (t << 5) + ord(character) | |
return t & 2147483647 | |
def compute_ptqrtoken(qrsig): | |
# 根据qrsig计算ptqrtoken,用于二维码验证 | |
e = 0 | |
for character in qrsig: | |
e += (e << 5) + ord(character) | |
return 2147483647 & e | |
def fetch_qr_code(self): | |
# 获取登录的二维码 | |
print("正在获取登录二维码...") | |
url = f'https://ssl.ptlogin2.qq.com/ptqrshow?appid=715030901&e=2&l=M&s=3&d=72&v=4&t={time.time()}&daid=73&pt_3rd_aid=0' | |
response = self.session.get(url) | |
qrsig = response.cookies.get('qrsig') | |
return response.content, qrsig | |
def display_qr(self, content): | |
# 显示登录用的二维码 | |
print("请扫描二维码登录。") | |
root = tk.Tk() | |
root.title("QQ扫码登录 by 小锋学长生活大爆炸") # 设置窗口标题 | |
icon_base64 = 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAABQhJREFUWEe9lm1MU1ccxp9zS4HSYkenRgGpG2wCYgVkRLeBmGimcWNsswsMFxD2YfswlzmCMLKED2TTbdmWDLOXDxS7zLoNPigZzBgcW8ROgZkJBUFgo04l6GjB8tKXe89yL7ShtYVbJZ4PNzfnPOf//53nvBJd3w2KByyUUG1JYmzD/YQhPMCk034/fYU+K6RhWBaAq5NjQUPEyJSIiVixPABBZ5/vsCwOBJN8tGcMMxPeU2a9Pvmj9bqlb2EcCuauhHM11tcX/LNYfGENrIkIxUaVIqDu/C0r7CwntBu/7cKsD0CgjgSk/njdvgOiAHbHrQyoM1wb9QD8+skF0YZRirbvdNodogFMJhM6Ojo8+uLiYuF/IcD6GQ4JapVQT0H5D2QSxiyVMBNCJW8UMxfi4DtNKr1OGxsUAA+hVqvR0tKC6upqvwA5aYvG9OQrKmkw63VaddAAa9euRWtra0CAG72/ecUsLCxET08PmpubPfXl5eUoKm34W1+nfTwogN7eXo9eq9X6dcDcfQ4SiURoGxkZQW5urvBfVVWFgoICGAwGnDp1CkUlPw3pda8miAYIJPRdAzyAVCqF1WqFxWLB9PQ08vPz7wUobRzQ1+3bIAogXhkRUNc5Ninsgij6H5SzUeAB+JHzhXeC4zjk5eUJAO4iOFDa2Kev25e8JMBiAnfbBtYEDduFDnY/RvvPCyM+efIkUlJS0N7ejqSkJAwNDSE6OhpNTU2ora1FUUmDSa/TpiwJIOYyepp2IQmDMDoLsXPLOq+YNTU1HgfKysqQlZUltBeXNnQfr9NqRAEsdRlFMza8GXEZRvtL6GtrFEa90O7PPj3iLHl+3SU4rOGK2Cw2RKXJPHb02ER2xp10zc7q4UAQoq9jKZyIxShkjsfwZ6sBcXFxczvEYBBWvmTWfDtv7/ZVIcpEzAyfQOiqp4x1OmPy3q3j/ZGqFXuUGw+N+4MQAMSsgXT2IuLZfnSwr+PS2e/vcaCv43T/E/HqDZRzwXHzrBBywMxOrVS65Cpl6BkipQfkyVW3fHN5HFhqCrZJb+Dl8AGcm9Xihcx4v8wz/d8YObtlm6eRos08astRKcMgl0nbQyj2y9IqvW5HD8BdEa+ircxVuFxpeC4j8OnqBUHRBoKcCZsDSkUof2/8RTn6WmT6+57TTvQUhMGOXMcPwhTkpMUsOmtTps8HwNqfxDyAj1iv2FxZ5K4T7YAULEpDzqDT8cqiDvCB7cMnulw285YAALxkj2Jz5S/8j+g1wIs1IWOIIcmYdbKwO1kwHAOGBajNeYWzsxOy8BDJI1EyV6ry5zVhZOqmMEqCHF+7KMXXkamVb3kAxOwCt8b3QaJarUDZu88O3zRbzbVf/SEkq317aIhhaHwgBwjFBXlq5TNeDoiF6Pqiy0uasHE1PjiUjWuDd36v+agtex7gX4ahsQEBABsYZod80+HOoKaAD+4wWsBZnAJEpIRCSiSIflR+a/y2zTpumXbwS31X+gSzK2OcEoB/Jc2dy34KBf1Q9EnoL0AkppBJrmI9NyjWQF9duehtuFgGNdf/XiZ7MR0UhWJJCHBavrnyRUIp79SDF0IInTYdzaQsfYNSuhuA95W5IAUBLk+HMdtXJR6+uyzJ/eHbTB+nUpcrGxxVgWHyCeB+GY0SQkrkmoqWuV36EMp45xFlaChXAUoqGIYcjNhU8aU77UMBcCezXTmySaGp6F445v8BLSC+zybZQ60AAAAASUVORK5CYII=' | |
# 将base64字符串转换回二进制数据,并设置为窗口图标 | |
icon_data = base64.b64decode(icon_base64) | |
icon_image = Image.open(BytesIO(icon_data)) | |
icon_photo = ImageTk.PhotoImage(icon_image) | |
root.tk.call('wm', 'iconphoto', root._w, icon_photo) # 设置窗口图标 | |
# 设置窗口的背景颜色 | |
root.configure(bg='white') | |
img = Image.open(BytesIO(content)) | |
img = img.resize((350, 350), Image.Resampling.LANCZOS) | |
photo = ImageTk.PhotoImage(img) | |
# 创建一个Frame来放置内容,增加边距效果 | |
frame = tk.Frame(root, bg='white', padx=20, pady=20) | |
frame.pack() | |
# 创建一个Label显示二维码,并放置到Frame中 | |
label = tk.Label(frame, image=photo, bg='white') | |
label.image = photo # 保持对photo的引用 | |
label.pack(pady=(0, 10)) # 在二维码和按钮之间添加一些垂直间距 | |
# 创建一个“已扫码”按钮,点击后关闭窗口,并美化按钮样式 | |
button = tk.Button(frame, text="已扫码", command=root.destroy, height=2, width=20, bg='#4CAF50', fg='white', font=('Arial', 12, 'bold')) | |
button.pack() | |
# 窗口居中显示 | |
root.update_idletasks() # 更新窗口状态,以获取准确的窗口大小 | |
width = root.winfo_width() | |
height = root.winfo_height() | |
x = (root.winfo_screenwidth() // 2) - (width // 2) | |
y = (root.winfo_screenheight() // 2) - (height // 2) | |
root.geometry(f'{width}x{height}+{x}+{y}') # 设置窗口大小和位置 | |
root.mainloop() | |
def check_qr_status(self, qrsig, ptqrtoken): | |
# 检查二维码登录状态 | |
print("正在检查二维码状态...") | |
while True: | |
url = f'https://ssl.ptlogin2.qq.com/ptqrlogin?u1=https%3A%2F%2Fqun.qq.com%2Fmanage.html%23click&ptqrtoken={ptqrtoken}&ptredirect=1&h=1&t=1&g=1&from_ui=1&ptlang=2052&action=0-0-{time.time()}&js_ver=20032614&js_type=1&login_sig=&pt_uistyle=40&aid=715030901&daid=73&' | |
response = self.session.get(url, cookies={'qrsig': qrsig}) | |
if '二维码已失效' in response.text: | |
print('二维码已失效。') | |
break | |
elif '登录成功' in response.text: | |
print('登录成功。') | |
return True | |
time.sleep(3) | |
return False | |
def verify_group_membership(self, skey, group_id): | |
# 验证登录用户是否为指定群组的成员 | |
print(f"正在验证群组 {group_id} 的成员资格...") | |
bkn = self.compute_bkn(skey) | |
url = 'https://qun.qq.com/cgi-bin/qun_mgr/get_group_list' | |
response = self.session.post(url, data={'bkn': bkn}) | |
print(response.text) | |
groups = re.findall(r'"gc":(\d+),"gn', response.text) | |
return group_id in groups | |
if __name__ == '__main__': | |
group_id = '722072237' # 要检查成员资格的QQ群ID | |
qq_login = QQGroupLogin() | |
qr_content, qrsig = qq_login.fetch_qr_code() | |
qq_login.display_qr(qr_content) | |
ptqrtoken = QQGroupLogin.compute_ptqrtoken(qrsig) | |
if qq_login.check_qr_status(qrsig, ptqrtoken): | |
skey = qq_login.session.cookies.get('skey') | |
if skey and qq_login.verify_group_membership(skey, group_id): | |
print('恭喜您,验证成功!') | |
else: | |
print('很遗憾,验证失败。') | |
else: | |
print('登录失败或二维码已失效。') |