Browse Source

Automatic Commit By liuyuqi

liuyuqi-dellpc 1 year ago
commit
b80ea5d2d0
8 changed files with 315 additions and 0 deletions
  1. 11 0
      README.md
  2. 3 0
      auto_cuit/__init__.py
  3. 9 0
      auto_cuit/api.py
  4. 198 0
      auto_cuit/cuit.py
  5. 67 0
      auto_cuit/libs/json_conf.py
  6. 7 0
      bin/auto_cuit
  7. 6 0
      conf/config.json
  8. 14 0
      main.py

+ 11 - 0
README.md

@@ -0,0 +1,11 @@
+## auto_cuit
+
+成都信息自动选课
+
+
+OCR Container
+
+
+```
+docker run -p 4006:4006 --name vercode jiyecafe/wecuit_py
+```

+ 3 - 0
auto_cuit/__init__.py

@@ -0,0 +1,3 @@
+
+def main():
+    pass

+ 9 - 0
auto_cuit/api.py

@@ -0,0 +1,9 @@
+
+# 获取课程列表 http://jwgl.cuit.edu.cn/eams/stdElectCourse!data.action?profileId=4134
+from tabnanny import check
+
+_host = "http://jwgl.cuit.edu.cn"
+getCourseList = _host + "/eams/stdElectCourse!data.action?profileId=%s"
+chooseCourse=_host+"/eams/stdElectCourse!batchOperator.action?profileId=%s"
+getCaptcha = _host + "/eams/captcha/image.action"
+checkCode = _host + "/eams/stdElectCourse!defaultPage.action"

+ 198 - 0
auto_cuit/cuit.py

@@ -0,0 +1,198 @@
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+'''
+@Contact :   liuyuqi.gov@msn.cn
+@Time    :   2022/05/31 01:04:55
+@License :   Copyright © 2017-2022 liuyuqi. All Rights Reserved.
+@Desc    :   cuit.edu.cn 自动选课
+'''
+from asyncio.log import logger
+import platform
+from auto_cuit.libs.json_conf import JsonConf
+from auto_cuit import api
+import requests
+import os
+import sys
+import re
+import json
+import time
+import random
+
+
+class Cuit(object):
+
+    def __init__(self, configPath="conf/config.josn"):
+        self.sess = requests.session()
+        self.conf = JsonConf(configPath)
+        self.sess.cookies = self.conf.data['cookies']
+        self.sess.headers = {
+            "X-Requested-With": "XMLHttpRequest",
+            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.0 Safari/537.36 Edg/84.0.521.0"
+        }
+
+    def run(self):
+        profiledId = self.conf.data['profiled_id']
+        courseName = self.conf.data['course_name']
+
+        # 验证码
+        cnt = 0
+        while True:
+            pic = self.getCaptcha()
+            ocrResult = self.postOCRPic(pic)
+            print('OCR: ' + ocrResult['result'])
+
+            checkResult = self.checkCaptcha(ocrResult['result'], profiledId)
+            if checkResult:
+                break
+            cnt += 1
+            time.sleep(0.5)
+            if cnt % 5 == 0:
+                print('验证码错误次数过多, 等待5秒')
+                time.sleep(5)
+
+        print('验证码检测通过,等待1秒')
+        time.sleep(1)
+        # 检测开放状态
+        print('获取lessonId')
+        lessonId = self.courseName2Id(profiledId, courseName)
+        if lessonId == None:
+            print('没有找到相关课程:' + courseName)
+            exit(1)
+
+        print('检测选课开放状态')
+        cnt = 0
+        while True:
+            if self.isAvailable(ocrResult['result'], profiledId):
+                break
+                pass
+            cnt += 1
+            print('没有到选课时间,等待5秒 - ' + str(cnt))
+            time.sleep(5)
+
+        # 抢课
+        print('开始抢课')
+
+        i = 0
+        while True:
+            i += 1
+            print(i)
+            if self.fuckCourse(str(profiledId), str(lessonId)):
+                break
+            time.sleep(0.5)
+            if i >= 20:
+                i = 0
+                if platform.system().lower() == 'linux':
+                    os.system("clear")
+                elif platform.system().lower() == 'windows':
+                    os.system("cls")
+                pass
+            pass
+        pass
+
+    def fuckCourse(self, profiledId, lessonId):
+        try:
+            body = {
+                "optype": "true",
+                "operator0": lessonId + ":true:0",
+                "lesson0": lessonId,
+                "schLessonGroup_" + lessonId: "undefined"
+            }
+            req = requests.post(api.chooseCourse % profiledId,
+                                data=body, timeout=5, allow_redirects=False)
+            req.encoding = 'utf-8'
+
+            html = req.text
+            ret = re.search(r"margin:auto;\">\n\t\t\t\t(.*)<\/br>", html)
+            if ret == None:
+                print("cookie过期")
+                exit(-1)
+                pass
+            print(ret.group(1))
+            req.close()
+            if '成功' in ret.group(1):
+                print('get')
+                return True
+        except Exception as err:
+            print("出错")
+            print(err)
+            return False
+        pass
+    pass
+
+    def getCaptcha(self):
+        try:
+            picReq = self.sess.get(url=api.getCaptcha, timeout=5)
+        except Exception as err:
+            print(err)
+        return picReq.content
+
+    def postOCRPic(self, pic):
+        url = self.ocrServer
+        files = {'captcha': pic}
+        data = {
+            'enctype': 'multipart/form-data',
+            'name': 'captcha'
+        }
+        ocrReq = requests.post(url=url, data=data, files=files)
+        return json.loads(ocrReq.content)
+
+    def checkCaptcha(self, captcha, profiledId):
+
+        data = {
+            'captcha_response': captcha,
+            'electionProfile.id': profiledId
+        }
+        try:
+            checkReq = requests.post(
+                url=api.checkCode, data=data, allow_redirects=False, timeout=5)
+        except Exception as err:
+            logger.error(err)
+
+        # print(checkReq.text)
+        # 未登录与验证码错误都是302,但Location去向不同
+        if checkReq.status_code == 200:
+            return True
+        elif 'sso' in checkReq.headers['Location']:
+            # 转到统一登录中心
+            print('cookie失效!!!')
+            exit(1)
+        return False
+
+    def isAvailable(self, captcha, profiledId):
+        '''
+        检测登录状态
+        '''
+        data = {
+            'captcha_response': captcha,
+            'electionProfile.id': profiledId
+        }
+        try:
+            checkReq = self.sess.post(
+                url=api.checkCode, data=data, allow_redirects=False, timeout=5)
+            # 未登录与验证码错误都是302,但Location去向不同
+            if checkReq.status_code == 200:
+                return '不在选课时间内' not in checkReq.text
+            elif 'sso' in checkReq.headers['Location']:
+                # 转到统一登录中心
+                print('cookie失效')
+                False
+        except Exception as err:
+            logger.error(err)
+            return False
+
+        return False
+
+    def courseName2Id(self, profileId, courseName):
+        try:
+            courseListReq = self.sess.get(
+                url=api.getCourseList % str(profileId), allow_redirects=False, timeout=5)
+        except Exception as err:
+            print(err)
+        courseList = courseListReq.text
+        jsData = execjs.compile(courseList)
+        lessonJSONs = jsData.eval('lessonJSONs')
+        # print(lessonJSONs)
+        for lesson in lessonJSONs:
+            if courseName in lesson['name']:
+                return lesson['id']
+        return None

+ 67 - 0
auto_cuit/libs/json_conf.py

@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+'''
+@Contact :   liuyuqi.gov@msn.cn
+@Time    :   2022/05/24 15:07:14
+@License :   Copyright © 2017-2022 liuyuqi. All Rights Reserved.
+@Desc    :   yaml util
+'''
+import os
+import json
+
+
+class JsonConf:
+    def __init__(self, config_path="conf/config.json"):
+        self.config_path = config_path
+        self.data=None
+        self.load()
+
+    def save(self, data):
+        with open(self.config_path, 'w') as json_file:
+            json_file.write(json.dumps(data, indent=4))
+
+    def load(self):
+        if not os.path.exists(self.config_path):
+            with open(self.config_path, 'w') as json_file:
+                pass
+        with open(self.config_path, encoding="utf-8") as json_file:
+            try:
+                self.data = json.load(json_file)
+            except Exception as e:
+                if(str(e).index("utf-8-sig") > 0):
+                    with open(self.config_path, encoding="utf-8-sig") as json_file:
+                        self.data = json.load(json_file)
+                        return self
+                else:
+                    print(e)
+            return self
+
+    def set(self, data_dict):
+        json_obj = self.load().data
+        for key in data_dict:
+            json_obj[key] = data_dict[key]
+        self.save(json_obj)
+
+    def get(self, key, default_val=""):
+        '''
+        配置文件获取key对象的值,如果没有设置就返回默认值
+        '''
+        try:
+            result = self.load().data[key]
+            return result
+        except Exception as e:
+            print(e)
+            return default_val
+
+    def get(self, jsonData, key, default_val=""):
+        try:
+            return jsonData[key]
+        except Exception as e:
+            return default_val
+
+    @staticmethod
+    def get(jsonData, key, default_val=""):
+        try:
+            return jsonData[key]
+        except Exception as e:
+            return default_val

+ 7 - 0
bin/auto_cuit

@@ -0,0 +1,7 @@
+#!/bin/python
+
+from auto_cuit.cuit import Cuit
+
+
+if __name__ == "__main__":
+    Cuit().run()

+ 6 - 0
conf/config.json

@@ -0,0 +1,6 @@
+{
+    "ocr_server": "http://127.0.0.1:4006/vercode",
+    "profiled_id": 4130,
+    "course_name": "数字孪生技术",
+    "cookie": ""
+}

+ 14 - 0
main.py

@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+'''
+@Contact :   liuyuqi.gov@msn.cn
+@Time    :   2022/05/31 01:10:42
+@License :   Copyright © 2017-2022 liuyuqi. All Rights Reserved.
+@Desc    :   main
+'''
+
+from auto_cuit.cuit import Cuit
+
+
+if __name__ == "__main__":
+    Cuit(configPath="conf/config.json").run()