一个天翼校园客户端的自动登陆器,原理上是向一个指定的URL发送Post即可拨号。
依赖
- Python3
- pip (pip3)
- Tesseract
Installation
pip install requests rsa pytesseract
How to run
options:
-u, --username TEXT 校园网登录账号
-p, --password TEXT 校园网登录密码
-i, --intervals INT 检测是否登录间隔 默认10
--userip TEXT 校园网的wlanuserip 默认自动获取
--acip TEXT 校园网的wlanacip 默认自动获取
--log-level TEXT 输出日志等级
--log-output TEXT 日志保存路径
Basic
$ python main.py -u [账号] -p [密码]
Set Interval Time
$ python main.py -u [账号] -p [密码] -i 10
Save Log
$ python main.py -u [账号] -p [密码] -log-output=$(pwd)/1.log
Code
import argparse
import logging
import signal
import sys
import time
from io import BytesIO
from urllib.parse import urlparse, parse_qsl
import pytesseract
import requests
import rsa
# import ddddocr
from PIL import Image
class CampusNet:
Req = requests.Session()
def __init__(self, username: str, password: str):
self.wlan_user_ip = None
self.wlan_ac_ip = None
self.username = username
self.password = password
self.timeout = 2
def needLogin(self):
r = requests.get(url="http://www.baidu.com/", allow_redirects=False, timeout=self.timeout)
times = r.elapsed.total_seconds()
logging.info('network connection times=%s s' % times)
need_login = 'location' in r.headers and 'enet.10000.gd.cn' in r.headers['location']
if need_login and self.wlan_ac_ip is None and self.wlan_user_ip is None:
enet_url = r.headers['location']
enet_query = dict(parse_qsl(urlparse(enet_url).query))
if 'wlanacip' in enet_query and 'wlanuserip' in enet_query:
self.setWlanACIP(enet_query['wlanacip'])
self.setWlanUserIP(enet_query['wlanuserip'])
logging.info("wlan_user_ip=%s, wlan_user_ip=%s" % (enet_query['wlanacip'], enet_query['wlanuserip']))
else:
raise "Fail to get enet ip. Please set them manually. "
return need_login
def _encryptByRSA(self, message: str):
e = "10001"
n = "b2867727e19e1163cc084ea57b9fa8406a910c6703413fa7df96c1acdca7b983a262e005af35f9485d92cd4c622eca4a14d6fd818adca5cae73d9d228b4ef05d732b41fb85f80af578a150ebd9a2eb5ececb853372ca4731ca1c8686892987409be3247f9b26cae8e787d8c135fc0652ec0678a5eda0c3d95cc1741517c0c9c3"
pubkey = rsa.PublicKey(e=int(e, 16), n=int(n, 16))
result = rsa.encrypt(message.encode(), pubkey).hex()
logging.info("RSA encrypt result=%s" % result)
return result
def getCodeImage(self):
code_image_url = "http://enet.10000.gd.cn:10001/common/image_code.jsp?time=%d" % time.time() * 1000
code_image = self.Req.get(url=code_image_url).content
logging.info("code image byte length={}".format(len(code_image)))
return code_image
def setCodeByTesseract(self):
image_obj = Image.open(BytesIO(self.getCodeImage()))
result = pytesseract.image_to_string(image_obj).strip()
logging.info("tesseract result={}".format(result))
self.setCode(result)
return result
# 取消注释可使用ddddocr,不过似乎ddddocr没法在安卓手机上使用(
# def setCodeByDdddocr(self):
# ocr = ddddocr.DdddOcr()
# result = ocr.classification(self.getCodeImage())
# logging.info("ddddocr result=%s" % result)
# self.setCode(result)
# return result
def login(self):
login_url = "http://enet.10000.gd.cn:10001/ajax/login"
headers = {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}
data = '{"userName":"%s","password":"%s","rand":"%s"}' % (self.username, self.password, self.code)
payload = {
"loginKey": self._encryptByRSA(data),
"wlanuserip": self.wlan_user_ip,
"wlanacip": self.wlan_ac_ip
}
result = self.Req.post(url=login_url, data=payload, headers=headers).json()
logging.info("login result={}".format(result))
return result
def setWlanUserIP(self, wlan_user_ip: str):
self.wlan_user_ip = wlan_user_ip
return self
def setWlanACIP(self, wlan_ac_ip: str):
self.wlan_ac_ip = wlan_ac_ip
return self
def setCode(self, code: str):
self.code = code
return self
def setTimeout(self, timeout: int):
self.timeout = timeout
return self
def handle_args():
parser = argparse.ArgumentParser(description='Campus Network Control.')
parser.add_argument('-u', '--username', type=str, required=True, help='Set the username')
parser.add_argument('-p', '--password', type=str, required=True, help='Set the password')
parser.add_argument('-i', '--intervals', type=int, help='Set the network test intervals (s)', default=10)
# parser.add_argument('-o', '--ocr', type=str, help='Set the ocr engine to use (tesseract, ddddocr)', default='tesseract')
parser.add_argument('--userip', type=str, help='Set the wlan user ip')
parser.add_argument('--acip', type=str, help='Set the wlan ac ip')
parser.add_argument('--log-level', type=str, help='Set the logging level', default='info')
parser.add_argument('--log-output', type=str, help='Set eht logging output')
args = parser.parse_args()
return args
def signal_exit(signum, frame):
logging.info('CampusNetwork stopped.')
sys.exit()
if __name__ == '__main__':
signal.signal(signal.SIGINT, signal_exit)
signal.signal(signal.SIGTERM, signal_exit)
try:
args = handle_args()
logging.basicConfig(
filename=args.log_output,
level=logging.getLevelName(args.log_level.upper()),
format="%(asctime)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S")
cnet = CampusNet(args.username, args.password)
while True:
if cnet.needLogin():
# 获取验证码,如果没够四位则重新获取
while True:
# code = None
code = cnet.setCodeByTesseract()
# if args.engine == 'tesseract':
# code = cnet.setCodeByTesseract()
# elif args.engine == 'ddddocr':
# code = cnet.setCodeByDdddocr()
# else:
# raise 'Failed to set ocr engine.'
if len(code) != 4:
logging.info("The result doesn't seem to be right so that need to get it again")
continue
result = cnet.login()
if 'resultCode' in result:
ret_code = int(result['resultCode'])
if ret_code == 0:
logging.info('登录成功')
break
elif ret_code == 11063000:
continue
raise "login failed."
time.sleep(args.intervals)
except Exception as e:
logging.error(e)
sys.exit(2)