├── .gitignore ├── LICENSE.txt ├── LoginUCAS.py ├── MyOCR.py ├── __init__.py ├── main.py ├── private.txt └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 hrwhisper 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LoginUCAS.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Date : 2017/2/1 3 | # @Author : hrwhisper 4 | import codecs 5 | import json 6 | import os 7 | import time 8 | from sys import exit 9 | import requests 10 | from MyOCR import image_to_string 11 | 12 | 13 | class UserNameOrPasswordError(Exception): 14 | pass 15 | 16 | 17 | class LoginUCAS(object): 18 | def __init__(self, use_onestop=True, vercode_save_name='certCode.jpg'): 19 | self.username, self.password = LoginUCAS._read_username_and_password() 20 | self.cnt = 0 21 | self.__BEAUTIFULSOUPPARSE = 'html5lib' # or use 'lxml' 22 | self.session = requests.session() 23 | self.vercode_save_name = vercode_save_name 24 | self.use_onestop = use_onestop 25 | self._init_login_url() 26 | 27 | def _init_login_url(self): 28 | if self.use_onestop: 29 | self._onestop_init() 30 | else: 31 | self._sep_init() 32 | 33 | def _onestop_init(self): 34 | self.url = { 35 | 'base_url': 'http://onestop.ucas.ac.cn/home/index', 36 | 'verification_code': None, 37 | 'login_url': 'http://onestop.ucas.ac.cn/Ajax/Login/0' 38 | } 39 | # self.session.get(self.url['base_url']) 40 | self.headers = { 41 | 'Host': 'onestop.ucas.ac.cn', 42 | "Connection": "keep-alive", 43 | 'Referer': 'http://onestop.ucas.ac.cn/home/index', 44 | 'X-Requested-With': 'XMLHttpRequest', 45 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36", 46 | } 47 | self.post_data = { 48 | "username": self.username, 49 | "password": self.password, 50 | "remember": 'checked', 51 | } 52 | 53 | def _sep_init(self): 54 | self.url = { 55 | 'base_url': 'http://sep.ucas.ac.cn/', 56 | 'verification_code': 'http://sep.ucas.ac.cn/changePic', 57 | 'login_url': "http://sep.ucas.ac.cn/slogin" 58 | } 59 | self.headers = { 60 | "Host": "sep.ucas.ac.cn", 61 | "Connection": "keep-alive", 62 | "Upgrade-Insecure-Requests": "1", 63 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36", 64 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 65 | "Accept-Encoding": "gzip, deflate, sdch", 66 | "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4", 67 | } 68 | self.post_data = { 69 | "userName": self.username, 70 | "pwd": self.password, 71 | "sb": "sb", 72 | "rememberMe": 1, 73 | } 74 | 75 | @classmethod 76 | def _read_username_and_password(cls): 77 | with codecs.open(r'./private.txt', "r", 'utf-8') as f: 78 | username = password = None 79 | for i, line in enumerate(f): 80 | if i == 0: 81 | line = bytes(line.encode('utf-8')) 82 | if line[:3] == codecs.BOM_UTF8: 83 | line = line[3:] 84 | username = line.decode('utf-8').strip() 85 | elif i == 1: 86 | password = line.strip() 87 | else: 88 | break 89 | return username, password 90 | 91 | def _download_verification_code(self): 92 | r = self.session.get(self.url['verification_code'], stream=True, headers=self.headers) 93 | with open(self.vercode_save_name, 'wb') as f: 94 | for chunk in r.iter_content(chunk_size=1024): 95 | if chunk: # filter out keep-alive new chunks 96 | f.write(chunk) 97 | f.flush() 98 | return self.vercode_save_name 99 | 100 | def _need_verification_code(self): 101 | r = self.session.get(self.url['base_url']) 102 | return r.text.find('验证码') != -1 103 | 104 | def login_sep(self): 105 | try: 106 | if not self.cnt: 107 | print('Login....' + self.url['base_url']) 108 | if self.use_onestop: 109 | html = self.session.post( 110 | self.url['login_url'], data=self.post_data, headers=self.headers).text 111 | res = json.loads(html) 112 | if not res['f']: 113 | raise UserNameOrPasswordError 114 | else: 115 | html = self.session.get(res['msg']).text 116 | print("登录成功 {}".format(self.cnt)) 117 | else: 118 | # 登录sep 119 | try: 120 | if self._need_verification_code(): 121 | cert_code = image_to_string(self._download_verification_code()) 122 | while not cert_code or len(cert_code) < 4: 123 | cert_code = image_to_string(self._download_verification_code()) 124 | self.post_data["certCode"] = cert_code 125 | html = self.session.post(self.url['login_url'], data=self.post_data, headers=self.headers).text 126 | if html.find('密码错误') != -1: 127 | raise UserNameOrPasswordError 128 | elif html.find('验证码错误') != -1: 129 | time.sleep(2) 130 | self.cnt += 1 131 | return self.login_sep() 132 | print("登录成功 {}".format(self.cnt)) 133 | except requests.exceptions.ConnectionError: 134 | print('请检查网络连接') 135 | exit(1) 136 | except UserNameOrPasswordError: 137 | print('用户名或者密码错误,请检查private文件') 138 | os.system("pause") 139 | exit(1) 140 | except requests.exceptions.ConnectionError: 141 | self.use_onestop = not self.use_onestop 142 | self._init_login_url() 143 | print("login time out, change to " + self.url['base_url']) 144 | self.cnt += 1 145 | if self.cnt > 20: 146 | print("估计是教务处挂了") 147 | exit(1) 148 | return self.login_sep() 149 | return self 150 | 151 | 152 | if __name__ == '__main__': 153 | LoginUCAS(True).login_sep() 154 | -------------------------------------------------------------------------------- /MyOCR.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Date : 2017/2/1 3 | # @Author : hrwhisper 4 | 5 | import os 6 | from sys import exit 7 | import re 8 | import subprocess 9 | from PIL import Image 10 | 11 | devnull = open(os.devnull, 'w') 12 | cut_size = 1 13 | 14 | 15 | def pre_process(func): 16 | def _wrapper(filename): 17 | image = Image.open(filename).point(lambda p: 255 if p > 127 else 0).convert("1") 18 | w, h = image.size 19 | image = image.crop((cut_size, cut_size, w - cut_size, h - cut_size)) 20 | save_name = filename # + '1.jpg' 21 | image.save(save_name) 22 | try: 23 | res = func(save_name) 24 | os.remove(save_name) 25 | return res 26 | except FileNotFoundError: 27 | print('请检查是否安装tesseract-OCR') 28 | os.remove(save_name) 29 | os.system("pause") 30 | exit(1) 31 | 32 | return _wrapper 33 | 34 | 35 | @pre_process 36 | def image_to_string(img): 37 | res = subprocess.check_output('tesseract ' + img + ' stdout', stderr=devnull).decode() # tesseract a.png result 38 | return (re.subn('\W', '', res.strip()) if res else ('', ''))[0].lower() 39 | 40 | 41 | if __name__ == '__main__': 42 | print(image_to_string('ucas_code1.jpg')) 43 | print(image_to_string('ucas_code2.jpg')) 44 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hrwhisper/ucas_course_helper/81ba93831398ef752c77f6f4a7d03e20f63cc411/__init__.py -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @Date : 2016/9/1 3 | # @Author : hrwhisper 4 | from __future__ import print_function 5 | import re 6 | import time 7 | 8 | import datetime 9 | 10 | import requests 11 | 12 | from LoginUCAS import LoginUCAS 13 | 14 | 15 | class NoLoginError(Exception): 16 | pass 17 | 18 | 19 | class NotFoundCourseError(Exception): 20 | pass 21 | 22 | 23 | class NotSelectCourseTime(Exception): 24 | pass 25 | 26 | 27 | class UcasCourse(object): 28 | def __init__(self): 29 | self.session = None 30 | self.headers = None 31 | self.jwxk_html = None 32 | self.course = UcasCourse._read_course_info() 33 | self._init_session() 34 | 35 | def _init_session(self): 36 | t = LoginUCAS().login_sep() 37 | self.session = t.session 38 | self.headers = t.headers 39 | self.login_jwxk() 40 | 41 | @classmethod 42 | def _read_course_info(self): 43 | with open("./private.txt") as f: 44 | courses = [] 45 | for i, line in enumerate(f): 46 | if i < 2: continue 47 | courses.append(line.strip().split()) 48 | return courses 49 | 50 | def login_jwxk(self): 51 | # 从sep中获取Identity Key来登录选课系统,进入选课选择课学院的那个页面 52 | url = "http://sep.ucas.ac.cn/portal/site/226/821" 53 | r = self.session.get(url, headers=self.headers) 54 | try: 55 | code = re.findall(r'"http://jwxk.ucas.ac.cn/login\?Identity=(.*)"', r.text)[0] 56 | except IndexError: 57 | raise NoLoginError 58 | 59 | url = "http://jwxk.ucas.ac.cn/login?Identity=" + code 60 | self.headers['Host'] = "jwxk.ucas.ac.cn" 61 | self.session.get(url, headers=self.headers) 62 | url = 'http://jwxk.ucas.ac.cn/courseManage/main' 63 | r = self.session.get(url, headers=self.headers) 64 | self.jwxk_html = r.text 65 | 66 | def get_course(self): 67 | # 获取课程开课学院的id,以及选课界面HTML 68 | html = self.jwxk_html 69 | regular = r'') != -1: 84 | raise NotSelectCourseTime 85 | url = 'http://jwxk.ucas.ac.cn' + \ 86 | re.findall(r'