Python 登入Eportal

# 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 找出要的資料算是比較可惜一點