Python自动化登录教务网系统,可实现自动化抢课(以GDTSU为例)

在这篇博客中,我们将深入解析一个基于Python的自动化登录系统的实现。该系统能够自动登录到某个教务系统页面,获取课程表和成绩信息,并将成绩保存到CSV文件中。本文将逐步讲解代码的各个模块及其功能。

1. 项目介绍

项目的主要目标是通过模拟登录教务系统,实现自动化操作,包括验证码识别、密码加密、登录页面获取、课程表查询以及成绩查询。整个项目基于Python实现,使用了requests库进行HTTP请求,lxml库进行HTML解析,以及一个验证码识别客户端库CaptureRecognition

2. 环境准备

首先,确保你已安装以下Python库:

pip install requests lxml

此外,项目中还用到了一个外部的Node.js脚本来处理密码的加密,这需要在系统中安装Node.js环境。
超级鹰平台自动输入验证码(可通过训练模型对验证码进行识别)

3. 项目结构

项目包含以下主要模块:

  • LoginAutomation类: 包含整个自动化流程的核心逻辑。
  • get_login_page方法: 负责获取登录页面的初始数据。
  • get_captcha_code方法: 处理验证码的获取和识别。
  • get_rsa_params方法: 获取RSA加密所需的公钥参数。
  • get_encrypted_password方法: 调用外部Node.js脚本进行密码加密。
  • post_login方法: 执行登录请求并验证登录结果。
  • query_courses方法: 获取课程表信息。
  • parse_course_data方法: 解析并打印课程表数据。
  • query_score方法: 获取成绩信息。
  • parse_score_data方法: 解析并保存成绩数据到CSV文件。
  • save_to_csv方法: 将成绩数据保存到CSV文件。
4. 核心代码解析
4.1. 初始化与配置加载

首先,我们定义了LoginAutomation类的初始化方法,用于配置基本信息,如用户名、密码、教务系统的基URL等。

def __init__(self, base_url, username, password, captcha_client):
    self.base_url = base_url
    self.username = username
    self.password = password
    self.captcha_client = captcha_client
    self.sess = requests.Session()
    self.timestamp = int(time.time() * 1000)
    self.csrf_token = None
    self.rsa_keys = None

配置文件config.json会加载用户的配置信息,包括用户名、密码和验证码识别系统的账号信息。

def load_config():
    with open('config.json', 'r') as f:
        config = json.load(f)
    return config
4.2. 获取登录页面和CSRF Token

在登录前,需要先获取页面的CSRF Token。这通过解析登录页面的HTML实现:

def get_login_page(self):
    url = f"{self.base_url}/xtgl/login_slogin.html"
    response = self.sess.get(url=url, allow_redirects=True)
    tree = etree.HTML(response.text)
    self.csrf_token = tree.xpath('//input[@id="csrftoken"]/@value')[0]
    logging.info("获取登录页面成功")
4.3. 获取并识别验证码

验证码是登录系统的一个重要防护措施。我们使用CaptureRecognition库来自动识别验证码:

def get_captcha_code(self):
    url = f"{self.base_url}/kaptcha?time={self.timestamp}"
    response = self.sess.get(url=url, allow_redirects=True)
    if response.status_code == 200:
        with open('captcha.png', 'wb') as f:
            f.write(response.content)
        logging.info("验证码图片获取成功")
        return self.captcha_client.PostPic(open('captcha.png', 'rb').read(), 1902)['pic_str']
    else:
        logging.error(f"获取验证码图片失败,状态码:{response.status_code}")
        return None
4.4. 获取RSA加密参数并加密密码

为了保证密码的安全性,系统使用RSA加密。我们首先通过请求获取RSA的公钥参数,然后使用外部Node.js脚本对密码进行加密:

def get_rsa_params(self):
    url = f"{self.base_url}/xtgl/login_getPublicKey.html?time={self.timestamp}&_={self.timestamp - 60}"
    response = self.sess.get(url=url)
    if response.status_code == 200:
        self.rsa_keys = response.json()
        modulus = self.rsa_keys.get('modulus')
        exponent = self.rsa_keys.get('exponent')
        with open('keys.json', 'w') as file:
            json.dump({'modulus': modulus, 'exponent': exponent}, file)
            file.close()
        logging.info("获取RSA参数成功")
    else:
        logging.error(f"获取RSA参数失败,状态码:{response.status_code}")

def get_encrypted_password(self):
    result = subprocess.run(['node', 'fetch.js'], capture_output=True, text=True)
    if result.returncode == 0:
        logging.info("密码加密成功")
        return result.stdout.strip()
    else:
        logging.error(f"密码加密失败:{result.stderr}")
        return None
4.5. 提交登录请求

通过post_login方法提交登录请求,验证登录是否成功:

def post_login(self, encrypted_password, captcha_code):
    url = f"{self.base_url}/xtgl/login_slogin.html?time={self.timestamp}"
    data = [
        ('csrftoken', self.csrf_token),
        ('language', 'zh_CN'),
        ('yhm', self.username),
        ('mm', encrypted_password),
        ('yzm', captcha_code)
    ]
    response = self.sess.post(url=url, data=data, allow_redirects=True)
    if "学生课表查询" in response.text:
        logging.info("登录成功")
        return True
    else:
        logging.error("登录失败")
        return False
4.6. 查询课程表与成绩

登录成功后,可以查询课程表与成绩,并将成绩保存到CSV文件中:

def query_courses(self):
    url = f"{self.base_url}/kbcx/xskbcx_cxXsgrkb.html"
    data = {
        'xnm': '2023',
        'xqm': '12',
        'kzlx': 'ck',
        'xsdm': ''
    }
    response = self.sess.post(url=url, data=data)
    response.encoding = 'utf-8'
    return response.json()

def parse_course_data(self, json_data):
    kb_list = json_data['kbList']
    logging.info("课程列表:")
    for kb_item in kb_list:
        logging.info(
            f"课程名称: {kb_item['kcmc']}, 教室: {kb_item['cdmc']}, 节次: {kb_item['jc']}, 教师: {kb_item['xm']}, 周次: {kb_item['zcd']}, 星期: {kb_item['xqjmc']}")

查询成绩并将其保存到CSV文件中:

def query_score(self):
    url = f"{self.base_url}/cjcx/cjcx_cxXsgrcj.html?doType=query"
    data = {
        'xnm': '2024',
        'xqm': '12',
        'kcbj': '',
        '_search': 'false',
        'nd': f'{self.timestamp}',
        'queryModel.showCount': '15',
        'queryModel.currentPage': '1',
        'queryModel.sortName': '',
        'queryModel.sortOrder': 'asc',
        'time': '1'
    }

    response = self.sess.post(url=url, data=data)
    response.encoding = 'utf-8'
    return response.json()

def parse_score_data(self, json_data):
    items_list = json_data['items']
    logging.info("成绩列表:")
    course_data = []
    for cj_item in items_list:
        logging.info(
            f"成绩: {cj_item['cj']}, 绩点: {cj_item['jd']}, 教师: {cj_item['jsxm']}, 考试性质: {cj_item['ksxz']}, 课程名称: {cj_item['kcmc']}, 课程类别: {cj_item['kcxzmc']}")
        course_data.append([
            cj_item['cj'],
            cj_item['jd'],
            cj_item['jsxm'],
            cj_item['ksxz'],
            cj_item['kcmc'],
            cj_item['kcxzmc']
        ])
    self.save_to_csv(course_data)

@staticmethod
def save_to_csv(course_data):
    file_path = 'scores.csv'
    is_empty = not os.path.exists(file_path) or os.path.getsize(file_path) == 0

    with open(file_path, mode='a', newline='', encoding='utf-8') as file:
        writer = csv.writer(file)
        if is_empty:
            writer.writerow(['成绩', '绩点', '教师', '考试性质', '课程名称', '课程类别'])
        writer.writerows(course_data)
5. 项目运行

将上述代码保存为Python脚本后,确保配置

文件和外部Node.js脚本已经准备好,运行主程序即可完成自动化登录及成绩查询:

if __name__ == "__main__":
    config = load_config()
    captcha_client = Chaojiying_Client(
        config['chaojiying_user'],
        config['chaojiying_pass'],
        config['chaojiying_soft_id']
    )
    login_automation = LoginAutomation(
        base_url="教务网地址",
        username=config['username'],
        password=config['password'],
        captcha_client=captcha_client
    )
    login_automation.run()

效果展示
在这里插入图片描述

6. 总结

这篇博客介绍了如何使用Python实现一个自动化登录系统,并且完成了从验证码识别到成绩保存的整个流程。通过这个项目,你可以学习到许多网络爬虫的实战技巧,例如请求伪装、验证码识别、RSA加密等。

希望这篇文章对你有所帮助,欢迎在实践中改进和扩展此项目!

需要源代码的同学添加

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值