• 使用Python开发选课脚本


    参考链接

    编号分类文章及链接介绍作者来源分类撰写日期收录日期
    L1参考python自动预约公众号hzp666CSDN博客2022-03-072023-09-11
    L2参考微信小程序抢票脚本躺平de咸鱼CSDN博客2022-07-062023-09-13

    前言: 孩子所在的小学每到开学季就会有周末课外非文化课的辅导班的抢课大战。某校通也是个小开发商,他们做的微信公众号上面的选课缴费程序因为有2-3千家长同时抢课,每次都会搞得网站接近瘫痪20分钟以上。运气好能抢到心仪的课,运气不好的时候一个课也选不到。而且,这个开发商还没有锁位机制,抢到课了,交钱慢了也不行。没有预留交钱的时间。几次想来搞一个抢课程序,但是都是觉得有点麻烦或者事情多给放弃了。趁着这次开学季,研究了一下,实验算是成功了。

    1. 工具准备

    2. 使用抓包工具fiddler获取微信公众号课程信息

    为了能够从脚本发出选课请求,需要获知如下内容:

    1. 选课接口URL

    2. Header参数,包括识别用户正常登录的cookies

    3. post数据格式

    4. post数据内容

      用电脑版微信登录某校通的微信公众号,注意提前在微信上绑定学生信息。
      进入选课界面,用fiddler跟踪公众号的信息。
      在这里插入图片描述
      通过访问选课界面,并通过其中的js脚本,可以定位到接口以及相应的header数据(最好保持完全一样)以及cookies数据。 post数据格式这一块,没有跑正式流程之前以为是json格式,后来才知道是web表单格式。至于数据内容,有四项GUID数据构成。分别是学生ID列表、课程组ID、课程ID、学校ID,除了课程ID外,其它几项的值从头到尾都是不变的,课程ID根据需要报名的课程去获取设置。
      正式抢课之前,由于接口没有开放,只是推测。所以正确的学生id没有拿到,格式也有错误。
      还好,抢课当天还有些课可以选,最后测试通过,算是正确完成。下个开学季可以随便选课了。

    在这里插入图片描述

    3. 编写python程序

    使用第三方的requests组件来发送表单数据。
    记得使用前安装 pip install requests

    核心代码及控制逻辑已经实现。同时选多个学生或者多个课程建议多线程进行,需自行实现。

    import sys
    import requests
    import time
    import json
    from pathlib import Path
    from marklog import info_logout, init_log
    
    
    class SelectCourse:
        over_flag = False
        # 学校id,不变
        course_school_id = 'COURSE_SCHOOL_ID'
        # 项目id,不变
        project_id = 'PROJECT_ID'
        # 学生码 ["GUID"]
        student_code = 'STUDENT_CODE'
        base_url = 'http://xxx.xxx'
    
        def select_course(self, course_id):
            referer_url = f'{self.base_url}/CourseSelection/CourseShow?CourseSchoolId={self.course_school_id}&' \
                        f'ProjectId={self.project_id}&id={course_id}'
            header = {
                "Accept": "application/json, text/javascript, */*; q=0.01",
                "Accept-Encoding": "gzip, deflate",
                "Accept-Language": "zh-CN,zh",
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
                  "Chrome/107.0.0.0 Safari/537.36 NetType/WIFI MicroMessenger/7.0.20.1781(0x6700143B)"
                  " WindowsWechat(0x63090621) XWEB/8391 Flue",
                "Referer": referer_url,  # 课程明细url
                # 采用表单模式发送请求内容
                "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
                "Connection": "keep-alive",
                "X-Requested-With": "XMLHttpRequest",
                "Host": "xxx.xxx",
                "Origin": "xxx.xxx",
                # Cookie信息放在Header里面
                "Cookie": "ASP.NET_SessionId=xxxxxxxxxxx; NetCXT=xxxxxxxxxxxx"
            }
    
            post_data = {"ProjectAndCourseLibraryId": course_id,
                         "StudentInfoLibraryIdList": f'["{self.student_code}"]',
                         "CourseSchoolId": self.course_school_id,
                         "ProjectId": self.project_id}
    
            # 执行选课操作
            post_url = f'{self.base_url}/CourseSelection/yyyyy'
            # requests库可以自动处理编码、
            r = requests.post(post_url, data=post_data, headers=header, verify=False)
    
            if r.status_code == 200:
                res = json.loads(r.text)
                if res["ErrorCode"] == 0:
                    info_logout(f'选课:{res["ErrorMsg"]},课程id: {course_id}')
                    self.set_over_flag(True)
                else:
                    info_logout(f'选课结果:{res["ErrorMsg"]},课程id: {course_id}')
                return True
            else:
                info_logout(r.text, True)
                info_logout(f'选课异常,错误代码{r.status_code}')
                return False
    
    if __name__ == '__main__':
        log_filename = Path(sys.argv[0]).stem + '.log'
        init_log(log_filename)
        info_logout(f'版本:0.1 2023/09/13 可以使用。注意实际使用前设置确认各个课程id、cookies等各个参数。', True)
        select = SelectCourse()
        course_ids = {'羽毛球A': 'zzz1',
                      '口风琴演奏A': 'zzz2',
                      '篮球D2': 'zzz3',
                      '语言表演C': 'zzz4'}
    
        interval = 100
        while not select.is_over():
            course_name = '羽毛球A'
            if select.select_course(course_ids[course_name]):
                info_logout(f'【{course_name}】 选课成功, 请马上进行支付', True)
                # 因为电脑微信不支持支付功能,只能在手机模拟器上来操作。不做自动支付。
            if not select.is_over():
                time.sleep(interval/1000)
                info_logout(f'休息{interval}ms')
        info_logout('选课结束', True)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    4. 实操结果截屏:

    在这里插入图片描述

  • 相关阅读:
    【Vant2】Tab标签页组件自动跳转的坑
    【数据标注】Label Studio用于机器学习标注
    前端进击笔记第十八节 实战:三天实现管理端系统
    【牛客 - 剑指offer】JZ7 重建二叉树 Java实现 两种方案(递归+非递归stack)
    实操客群分层|无监督训练与有监督评估,面试中这两大类风控模型最会被问到的问题
    mybatis-plus 自带QueryWrapper自定义sql实现复杂查询
    HTML 中使用 JavaScript 的具体方式
    Spring框架(三)
    nacos(一):简介
    WorkPlus打造安全专属的移动数字化航空母舰,助力企业全面掌控业务和生态
  • 原文地址:https://blog.csdn.net/sitebus/article/details/132863702