# Login Class
import requests
import urllib3
import re
from urllib.parse import urlparse
class EportalLogin:
def __init__(self,user,password):
urllib3.disable_warnings()
# Variable #
self.session = requests.Session()
self.user = user
self.password = password
# const Variable #
self.validCode = 'https://sso.nutc.edu.tw/ePortal/Validation_Code.aspx'
self.loginUrl = 'https://sso.nutc.edu.tw/ePortal/Default.aspx'
self.defaultUrl = 'https://sso.nutc.edu.tw/ePortal/myarea/MyArea.aspx'
def _getHiddenValue(self):
try:
r = self.session.get(url=self.loginUrl, headers=self._headers(self.loginUrl))
r.encoding='utf-8'
result = {}
result['VIEWSTATE'] = re.findall(r'<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="(.*?)" \/>',r.text)[0]
result['EVENTTARGET'] = re.findall(r'<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="(.*?)" \/>',r.text)[0]
result['EVENTARGUMENT'] = re.findall(r'<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="(.*?)" />',r.text)[0]
result['EVENTVALIDATION'] = re.findall(r'<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="(.*?)" \/>',r.text)[0]
result['VIEWSTATEGENERATOR'] = re.findall(r'<input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="(.*?)" \/>',r.text)[0]
return result
except:
return None
def _headers(self,url):
parse_object = urlparse(url)
result = {
'Connection' : 'keep-alive',
'Host' : parse_object.netloc,
'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
}
return result
def _generateLoginData(self,value,valid_code):
data = {
'__EVENTTARGET' : value['EVENTTARGET'],
'__EVENTARGUMENT' : value['EVENTARGUMENT'],
'__VIEWSTATE' : value['VIEWSTATE'],
'__VIEWSTATEGENERATOR' : value['VIEWSTATEGENERATOR'],
'__EVENTVALIDATION' : value['EVENTVALIDATION'],
'ctl00$ContentPlaceHolder1$Login.x' : '0',
'ctl00$ContentPlaceHolder1$Login.y' : '0',
'ctl00$ContentPlaceHolder1$Account' : self.user,
'ctl00$ContentPlaceHolder1$Password' : self.password,
'ctl00$ContentPlaceHolder1$ValidationCode' : valid_code
}
return data
# if success return session else return None
def login(self):
try:
print('[程式] 正在建立連線...')
captcha = self.session.get(self.validCode,headers=self._headers(self.validCode))
with open('.//ValidCode.png','wb') as f:
f.write(captcha.content)
f.flush()
f.close()
valid_code = input("[網站請求] 請輸入驗證碼(.\ValidCode.png): ").strip()
hiddenValue = self._getHiddenValue()
data = self._generateLoginData(hiddenValue,valid_code)
self.session.post(self.loginUrl,data=data,headers=self._headers(self.loginUrl))
response = self.session.get(self.defaultUrl,headers=self._headers(self.defaultUrl))
response.encoding = 'utf-8'
eportalUrl = re.search(r'"https:\/\/ais.nutc.edu.tw\/student\/Verifyv2.aspx\?Ticket=\w+"',response.text).group(0).strip('"')
if eportalUrl:
print("[網站回應] Eprotal登入成功!接下來進行學生管理系統的認證")
res = self.session.get(eportalUrl,headers=self._headers(eportalUrl),verify=False)
print('[網站回應] 登錄成功')
#print(res.text)
return self.session
else:
print("[網站回應] Eprotal登入不成功,可能是驗證碼或是帳號密碼輸入錯誤...")
return None
except:
print("登入出現問題。")
return None
由於中科大 E-portal 是使用 asp.net 開發
觀察送出時的POST資料總共有VIEWSTATE、EVENTTARGET、EVENTARGUMENT、EVENTVALIDATION、VIEWSTATEGENERATOR 要取得(這幾筆資料真的超坑)
所以要先用Session(可以保持Cookie)取得這幾筆資料後再行登入動作。
登入 E-Portal 後要取得學生管理系統的認證,所以用正則表達式 (Regex) 篩選出網址後送出GET請求就算成功登入了。
登入後就可以用返回的session去做爬蟲的動作取得課程資料、成績資訊、缺曠資訊等等
又或是寫一個搶課系統 ? 可以試試看… 今年還是沒有選到通識課…
但是要等到下一次系統開放的時候寫了
因為是 asp.net 沒辦法用一般檢查 JS 找出要的資料算是比較可惜一點
