【教程】最新可用! Python实现QQ扫码登录的群验证

Python
12
0
0
2024-11-15
标签   Python实践

目录

背景说明

使用效果

参考代码

扫码登录(备份)

背景说明

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()

    @staticmethod
    def compute_bkn(skey):
        # 计算bkn,某些请求所需的参数
        t = 5381
        for character in skey:
            t += (t << 5) + ord(character)
        return t & 2147483647

    @staticmethod
    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('登录失败或二维码已失效。')