小伙用Python入侵學校教務系統進行搶課

  • 時間:2018-10-28 22:54 作者:嬌兮心有之 來源:嬌兮心有之 閱讀:298
  • 掃一掃,手機訪問
摘要:想要學習Python。關注小編頭條號,私信【學習資料】,就可免費領取一整套系統的板Python學習教程!嘗試登錄首先我們打開學校的教務系統,隨意輸入,而后提交表單,打開Chrome的開發者工具中的Network準備抓包把css 圖片之類的過濾掉,發現了default.aspx這個東西假如你們學校教務
小伙用Python入侵學校教務系統進行搶課

想要學習Python。關注小編頭條號,私信【學習資料】,就可免費領取一整套系統的板Python學習教程!

嘗試登錄

首先我們打開學校的教務系統,隨意輸入,而后提交表單,打開Chrome的開發者工具中的Network準備抓包

小伙用Python入侵學校教務系統進行搶課

把css 圖片之類的過濾掉,發現了default.aspx這個東西

假如你們學校教務系統不使用Cookie則會是這樣

我們可以發現,真實的請求地址為http://110.65.10.xxx/(bdq1aj45lpd42o55vqpfgpie)/default2.aspx

隨后我們發現這個網址括號圍起來的一串信息有點詭異,而且每次進入的時候信息都不一樣,經過資料查詢,這是一種ASP.NET不使用Cookie會話管理的技術。

不使用 Cookie 的 ASP.NET 會話管理

那這樣就很好辦了,我們只要要登錄時記錄下這個數據就可保持登錄狀態。

經過測試發現,我們可以隨意偽造一個會話信息就可一直保持登錄狀態,但是為了表現模擬登錄的科學性,我們需要先獲取該會話信息。

假如你們學校教務系統使用Cookie則會是這樣

小伙用Python入侵學校教務系統進行搶課

服務器會返回一個Cookie值,而后在本地保存,這與下面的會不相同。

獲取會話信息(不使用Cookie)

這里我們要使用requests庫,并且要偽造header的UA信息

經過測試發現,我們只訪問學校的IP地址,會自動重定向至有會話信息的網址,所以我們先訪問一下IP地址。

class Spider:
def __init__(self, url):
self.__uid = ''
self.__real_base_url = ''
self.__base_url = url
self.__headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36',
}
def __set_real_url(self):
request = requests.get(self.__base_url, headers=self.__headers)
real_url = request.url
self.__real_base_url = real_url[:len(real_url) - len('default2.aspx')]
return request

上面獲取的url即為帶有會話信息的網址,保存的url格式為http://110.65.10.xxx/(bdq1aj45lpd42o55vqpfgpie)/

保存為這樣的格式是由于我們要訪問其余地址

獲取會話信息(使用Cookie)

有些學校的教務系統是使用Cookie的,我們只要要初次get請求時保存Cookie就可,而后此后一直使用該cookie

def get_cookie():
request = requests.get('http://xxx.xxx.xxx.xxx') #以某教務系統為例子
cookie = requets.cookie
return cookie

而requests中使用Cookie很簡單

只要要這樣

def use_cookie(cookie):
request = requests.get('http://xxx.xxx.xxx.xxx',cookie=cookie)

因為我們學校采用的是無Cookie方案,所以下面的代碼均沒有發送Cookie,假如你的學校采用了Cookie,只要要像我上面這樣發送Cookie就行了。

而假如你們學校使用Cookie,就不必獲取帶有會話信息的地址了,直接存儲Cookie就可。

或者者也可以使用requests的Session自動管理睬話信息,這樣文章下面的代碼的請求一律改成Session的請求就可,但是首先需要在類的初始化方法中初始化。

def __init__(self):
self.session = requests.Session()

而后我們首先訪問一次網站就可獲取Cookie并且儲存

def get(self):
r = self.session.get(url,headers=headers)

更多的用法可以查詢文檔

驗證碼的解決

分析r返回的文本信息

發現驗證碼的標簽的資源地址為 src=”CheckCode.aspx” ,我們可以直接requests而后下載驗證碼圖片,下載圖片的一種優雅的方式如下

def __get_code(self):
request = requests.get(self.__real_base_url + 'CheckCode.aspx', headers=self.__headers)
with open('code.jpg', 'wb')as f:
f.write(request.content)
im = Image.open('code.jpg')
im.show()
print('Please input the code:')
code = input()
return code

上面的代碼把圖片保存為code.jpg,Python有一個Image板塊,可以實現自動打開圖片

這樣驗證碼就展現出來了,我們人工輸入或者者轉入打碼平臺皆可

登錄數據的構造

這是上面抓的登錄post的數據包,

小伙用Python入侵學校教務系統進行搶課

發現有信息無法被解碼,應該是gb2312編碼,查看解碼前的編碼

小伙用Python入侵學校教務系統進行搶課

而后將不能解碼的代碼復制能夠解碼的地方

發現%D1%A7%C9%FA編碼解碼后為學生

這也就對應了學生選項的登錄

學號和密碼和驗證碼能夠顯而易見解知道是哪些信息,但是我們發現有__VIEWSTATE這一項

查找一下,這是一個表單隱藏信息,我們可以用BeautifulSoup庫解析可以得出該一項數據的值

小伙用Python入侵學校教務系統進行搶課

這是完整的登錄數據包,

def __get_login_data(self, uid, password):
self.__uid = uid
request = self.__set_real_url()
soup = BeautifulSoup(request.text, 'lxml')
form_tag = soup.find('input')
__VIEWSTATE = form_tag['value']
code = self.__get_code()
data = {
'__VIEWSTATE': __VIEWSTATE,
'txtUserName': self.__uid,
'TextBox2': password,
'txtSecretCode': code,
'RadioButtonList1': '學生'.encode('gb2312'),
'Button1': '',
'lbLanguage': '',
'hidPdrs': '',
'hidsc': '',
}
return data

登錄

假如登錄完成了,如何判斷能否登錄成功呢?我們從登錄成功返回的界面發現有姓名這一標簽,而我們等一下也是需要學生姓名,所以我們用這個根據來判斷能否登錄成功。

小伙用Python入侵學校教務系統進行搶課

代碼如下,進行了驗證碼客戶名和密碼的提醒信息判別

def login(self,uid,password):
while True:
data = self.__get_login_data(uid, password)
request = requests.post(self.__real_base_url + 'default2.aspx', headers=self.__headers, data=data)
soup = BeautifulSoup(request.text, 'lxml')
try:
name_tag = soup.find(id='xhxm')
self.__name = name_tag.string[:len(name_tag.string) - 2]
print('歡迎'+self.__name)
except:
print('Unknown Error,try to login again.')
time.sleep(0.5)
continue
finally:
return True

獲取選課信息

接下來就是獲取選課信息了,這里我們以校公選課為例子,點擊進去,進行抓包,headers沒有什么好注意的,我們只用關注get發送的包就可

小伙用Python入侵學校教務系統進行搶課

小伙用Python入侵學校教務系統進行搶課

小伙用Python入侵學校教務系統進行搶課

發現有學號與姓名與gnmkdm這一項,姓名我們需要編碼為gb2312的形式才能進行傳送

這里我們注意headers需要新添加Referer項也就是當前訪問的網址,才能進行請求

def __enter_lessons_first(self):
data = {
'xh': self.__uid,
'xm': self.__name.encode('gb2312'),
'gnmkdm': 'N121103',
}
self.__headers['Referer'] = self.__real_base_url + 'xs_main.aspx?xh=' + self.__uid
request = requests.get(self.__real_base_url + 'xf_xsqxxxk.aspx', params=data, headers=self.__headers)
self.__headers['Referer'] = request.url
soup = BeautifulSoup(request.text, 'lxml')
self.__set__VIEWSTATE(soup)

注意到上面有一個設置VIEWSTATE值的函數,這里等下在選課構造數據包的時候會講

模擬選課

隨意選一門課,而后提交,抓包,看一下有什么數據發送

小伙用Python入侵學校教務系統進行搶課

小伙用Python入侵學校教務系統進行搶課

前三個值可以在原網頁中input標簽中找到,因為前兩項為空,就不獲取了,而第三項我們使用soup解析獲取就可,因為這個操作是每請求一次就變化的,我們寫成一個函數,每次請求完成就設置一次。

小伙用Python入侵學校教務系統進行搶課

 def __set__VIEWSTATE(self, soup):
__VIEWSTATE_tag = soup.find('input', attrs={'name': '__VIEWSTATE'})
self.__base_data['__VIEWSTATE'] = __VIEWSTATE_tag['value']

而其余數據,我們通過搜索響應網頁即可以知道他們是干什么用的,這里我只說明我們要用的數據。

TextBox1為搜索框數據,我們可以用這個來搜索課程,dpkcmcGrid:txtPageSize為一頁顯示多少數據,經過測試,服務器最多響應200條。

值得注意的是ddl_xqbs這個校區數據信息,我所在的校區的數字代號為2,也許不同學校設置有所不同,需要自定義一下,也可以從網頁中獲取

下面是基礎數據包,因為我們搜索課程與選擇課程都要使用這個基礎數據包,所以我們直接在init函數里面新添加

 self.__base_data = {
'__EVENTTARGET': '',
'__EVENTARGUMENT': '',
'__VIEWSTATE': '',
'ddl_kcxz': '',
'ddl_ywyl': '',
'ddl_kcgs': '',
'ddl_xqbs': '2',
'ddl_sksj': '',
'TextBox1': '',
'dpkcmcGrid:txtChoosePage': '1',
'dpkcmcGrid:txtPageSize': '200',
}

而后我們關注一下這條數據,我們搜索一下,發現這是課程的提交選課的代碼,所以我們也可以直接從網頁中獲取,而on表示選項被選上

小伙用Python入侵學校教務系統進行搶課

kcmcGrid:_ctl2:xk:'on'

搜索課程

課程有很多信息,比方名字,上課時間,地點,這些東西確定好了才知道選的是哪門課,所以我們先新建一個類來存儲信息

 class Lesson:
def __init__(self, name, code, teacher_name, Time, number):
self.name = name
self.code = code
self.teacher_name = teacher_name
self.time = Time
self.number = number
def show(self):
print('name:' + self.name + 'code:' + self.code + 'teacher_name:' + self.teacher_name + 'time:' + self.time)

有了這個類,我們即可以進行搜索課程了,具體代碼看下面代碼,解析網頁內容就不細講了。

def __search_lessons(self, lesson_name=''):
self.__base_data['TextBox1'] = lesson_name.encode('gb2312')
request = requests.post(self.__headers['Referer'], data=self.__base_data, headers=self.__headers)
soup = BeautifulSoup(request.text, 'lxml')
self.__set__VIEWSTATE(soup)
return self.__get_lessons(soup)
def __get_lessons(self, soup):
lesson_list = []
lessons_tag = soup.find('table', id='kcmcGrid')
lesson_tag_list = lessons_tag.find_all('tr')[1:]
for lesson_tag in lesson_tag_list:
td_list = lesson_tag.find_all('td')
code = td_list[0].input['name']
name = td_list[1].string
teacher_name = td_list[3].string
Time = td_list[4]['title']
number = td_list[10].string
lesson = self.Lesson(name, code, teacher_name, Time, number)
lesson_list.append(lesson)
return lesson_list

進行選課

選課我們只需將lesson_list傳入就可,這就是我們之前創立的Lesson類的實例的列表,’Button’的內容為’ 提交 ‘,這兩邊各有一個空格,完事后我們可以進行發送請求進行選課。

這里我們用正則提取了錯誤信息,比方選課時間未到、上課時間沖突這些錯誤信息來提醒客戶,我們還解析了網頁的已選課程,這里也不細講了,都是基礎的網頁解析。

 def __select_lesson(self, lesson_list):
data = copy.deepcopy(self.__base_data)
data['Button1'] = ' 提交 '.encode('gb2312')
for lesson in lesson_list:
code = lesson.code
data[code] = 'on'
request = requests.post(self.__headers['Referer'], data=data, headers=self.__headers)
soup = BeautifulSoup(request.text, 'lxml')
self.__set__VIEWSTATE(soup)
error_tag = soup.html.head.script
if not error_tag is None:
error_tag_text = error_tag.string
r = "alert\('(.+?)'$$;"
for s in re.findall(r, error_tag_text):
print(s)
print('已選課程:')
selected_lessons_pre_tag = soup.find('legend', text='已選課程')
selected_lessons_tag = selected_lessons_pre_tag.next_sibling
tr_list = selected_lessons_tag.find_all('tr')[1:]
for tr in tr_list:
td = tr.find('td')
print(td.string)

總結

這次我們完成了模擬正方教務系統選課的過程,因為這個教務系統技術比較陳舊,所以比較好弄,事實上搶課的時候用Fiddler就可完成操作,由于我們只要要提前登錄而后記錄網址就可。

想要學習Python。關注小編頭條號,私信【學習資料】,就可免費領取一整套系統的板Python學習教程

  • 全部評論(0)
最新發布的資訊信息
【系統環境|數據庫】零基礎如何快速學好大數據?(2019-06-29 12:27)
【系統環境|Linux】零基礎如何學好大數據?必備需要學習知識(2019-06-18 11:54)
【系統環境|】Hadoop環境中管理大數據存儲八大技巧(2019-06-15 11:01)
【系統環境|服務器應用】現在國內IT行業是不是程序員過多了?(2019-06-11 06:34)
【系統環境|服務器應用】新貴 Flutter(2) 自己設置 Widget(2019-06-11 06:34)
【系統環境|服務器應用】Android完整知識體系路線(菜鳥-資深-大牛必進之路)(2019-06-11 06:34)
【系統環境|服務器應用】Java程序員小伙經歷三個月備戰,終獲阿里offer(2019-06-11 06:34)
【系統環境|服務器應用】每日一問:談談對 MeasureSpec 的了解(2019-06-11 06:34)
【系統環境|服務器應用】【科普】晶體管-1(2019-06-11 06:34)
【系統環境|服務器應用】一個很多人都會答錯的java基礎題(2019-06-11 06:33)
手機二維碼手機訪問領取大禮包
返回頂部
双色球号码300期遗传走势图