自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

用Python實(shí)現(xiàn)模擬登錄正方教務(wù)系統(tǒng)搶課

開發(fā) 后端
最近學(xué)校開始選課,但是如果選課時(shí)間與自己的事情沖突,這時(shí)候就可以使用Python腳本自助搶課,搶課的第一步即是模擬登錄,需要模擬登錄后保存登錄信息然后再進(jìn)行操作。

最近學(xué)校開始選課,但是如果選課時(shí)間與自己的事情沖突,這時(shí)候就可以使用Python腳本自助搶課,搶課的***步即是模擬登錄,需要模擬登錄后保存登錄信息然后再進(jìn)行操作。

而且整個(gè)流程是比較簡單,這是因?yàn)檎浇虅?wù)系統(tǒng)是比較舊的,全文的IP地址部分遮擋,請(qǐng)換成你們學(xué)校的IP地址。

嘗試登錄

首先我們打開學(xué)校的教務(wù)系統(tǒng),隨便輸入,然后提交表單,打開Chrome的開發(fā)者工具中的Network準(zhǔn)備抓包

 


把css 圖片之類的過濾掉,發(fā)現(xiàn)了default.aspx這個(gè)東西

如果你們學(xué)校教務(wù)系統(tǒng)不使用Cookie則會(huì)是這樣

 

我們可以發(fā)現(xiàn),真實(shí)的請(qǐng)求地址為http://110.65.10.xxx/(bdq1aj45lpd42o55vqpfgpie)/default2.aspx

隨后我們發(fā)現(xiàn)這個(gè)網(wǎng)址括號(hào)圍起來的一串信息有點(diǎn)詭異,而且每次進(jìn)入的時(shí)候信息都不一樣,經(jīng)過資料查詢,這是一種ASP.NET不使用Cookie會(huì)話管理的技術(shù)。

那這樣就很好辦了,我們只需要登錄時(shí)記錄下這個(gè)數(shù)據(jù)即可保持登錄狀態(tài)。

經(jīng)過測試發(fā)現(xiàn),我們可以隨便偽造一個(gè)會(huì)話信息即可一直保持登錄狀態(tài),但是為了體現(xiàn)模擬登錄的科學(xué)性,我們需要先獲取該會(huì)話信息。

如果你們學(xué)校教務(wù)系統(tǒng)使用Cookie則會(huì)是這樣

 

服務(wù)器會(huì)返回一個(gè)Cookie值,然后在本地保存,這與下面的會(huì)不相同。

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

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

經(jīng)過測試發(fā)現(xiàn),我們只訪問學(xué)校的IP地址,會(huì)自動(dòng)重定向至有會(huì)話信息的網(wǎng)址,所以我們先訪問一下IP地址。

  1. class Spider: 
  2.     def __init__(self, url): 
  3.         self.__uid = '' 
  4.         self.__real_base_url = '' 
  5.         self.__base_url = url 
  6.         self.__headers = { 
  7.             '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'
  8.         } 
  9.     def __set_real_url(self): 
  10.         request = requests.get(self.__base_url, headers=self.__headers) 
  11.         real_url = request.url 
  12.         self.__real_base_url = real_url[:len(real_url) - len('default2.aspx')] 
  13.         return request 

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

保存為這樣的格式是因?yàn)槲覀円L問其他地址

獲取會(huì)話信息(使用Cookie)

有些學(xué)校的教務(wù)系統(tǒng)是使用Cookie的,我們只需要***get請(qǐng)求時(shí)保存Cookie即可,然后此后一直使用該cookie

  1. def get_cookie(): 
  2.     request = requests.get('http://xxx.xxx.xxx.xxx') #以某教務(wù)系統(tǒng)為例子 
  3.     cookie = requets.cookie 
  4.     return cookie 

而requests中使用Cookie很簡單

只需要這樣

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

由于我們學(xué)校采用的是無Cookie方案,所以下面的代碼均沒有發(fā)送Cookie,如果你的學(xué)校采用了Cookie,只需要像我上面這樣發(fā)送Cookie就行了。

而如果你們學(xué)校使用Cookie,就不必獲取帶有會(huì)話信息的地址了,直接存儲(chǔ)Cookie即可。

驗(yàn)證碼的處理

分析r返回的文本信息

發(fā)現(xiàn)驗(yàn)證碼的標(biāo)簽的資源地址為 src=”CheckCode.aspx” ,我們可以直接requests然后下載驗(yàn)證碼圖片,下載圖片的一種優(yōu)雅的方式如下

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

上面的代碼把圖片保存為code.jpg,Python有一個(gè)Image模塊,可以實(shí)現(xiàn)自動(dòng)打開圖片

這樣驗(yàn)證碼就展示出來了,我們?nèi)斯ぽ斎牖蛘咿D(zhuǎn)入打碼平臺(tái)皆可

登錄數(shù)據(jù)的構(gòu)造

這是上面抓的登錄post的數(shù)據(jù)包,

 

發(fā)現(xiàn)有信息無法被解碼,應(yīng)該是gb2312編碼,查看解碼前的編碼

 

然后將不能解碼的代碼復(fù)制能夠解碼的地方

發(fā)現(xiàn)%D1%A7%C9%FA編碼解碼后為學(xué)生

這也就對(duì)應(yīng)了學(xué)生選項(xiàng)的登錄

學(xué)號(hào)和密碼和驗(yàn)證碼能夠顯而易見地知道是哪些信息,但是我們發(fā)現(xiàn)有__VIEWSTATE這一項(xiàng)

查找一下,這是一個(gè)表單隱藏信息,我們可以用BeautifulSoup庫解析可以得出該一項(xiàng)數(shù)據(jù)的值

 

這是完整的登錄數(shù)據(jù)包,

  1. def __get_login_data(self, uid, password): 
  2.     self.__uid = uid 
  3.     request = self.__set_real_url() 
  4.     soup = BeautifulSoup(request.text, 'lxml'
  5.     form_tag = soup.find('input'
  6.     __VIEWSTATE = form_tag['value'
  7.     code = self.__get_code() 
  8.     data = { 
  9.         '__VIEWSTATE': __VIEWSTATE, 
  10.         'txtUserName': self.__uid, 
  11.         'TextBox2'password
  12.         'txtSecretCode': code, 
  13.         'RadioButtonList1''學(xué)生'.encode('gb2312'), 
  14.         'Button1'''
  15.         'lbLanguage'''
  16.         'hidPdrs'''
  17.         'hidsc'''
  18.     } 
  19.     return data 

登錄

如果登錄完成了,如何判斷是否登錄成功呢?我們從登錄成功返回的界面發(fā)現(xiàn)有姓名這一標(biāo)簽,而我們等一下也是需要學(xué)生姓名,所以我們用這個(gè)根據(jù)來判斷是否登錄成功。

 

代碼如下,進(jìn)行了驗(yàn)證碼用戶名和密碼的提示信息判別

  1. def login(self,uid,password): 
  2.     while True
  3.         data = self.__get_login_data(uid, password
  4.         request = requests.post(self.__real_base_url + 'default2.aspx', headers=self.__headers, data=data) 
  5.         soup = BeautifulSoup(request.text, 'lxml'
  6.         try: 
  7.             name_tag = soup.find(id='xhxm'
  8.             self.__name = name_tag.string[:len(name_tag.string) - 2] 
  9.             print('歡迎'+self.__name) 
  10.         except
  11.             print('Unknown Error,try to login again.'
  12.             time.sleep(0.5) 
  13.             continue 
  14.         finally: 
  15.             return True 

獲取選課信息

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

 

 

發(fā)現(xiàn)有學(xué)號(hào)與姓名與gnmkdm這一項(xiàng),姓名我們需要編碼為gb2312的形式才能進(jìn)行傳送

這里我們注意headers需要新增Referer項(xiàng)也就是當(dāng)前訪問的網(wǎng)址,才能進(jìn)行請(qǐng)求

  1. def __enter_lessons_first(self): 
  2.     data = { 
  3.         'xh': self.__uid, 
  4.         'xm': self.__name.encode('gb2312'), 
  5.         'gnmkdm''N121103'
  6.     } 
  7.     self.__headers['Referer'] = self.__real_base_url + 'xs_main.aspx?xh=' + self.__uid 
  8.     request = requests.get(self.__real_base_url + 'xf_xsqxxxk.aspx', params=data, headers=self.__headers) 
  9.     self.__headers['Referer'] = request.url 
  10.     soup = BeautifulSoup(request.text, 'lxml'
  11.     self.__set__VIEWSTATE(soup) 

注意到上面有一個(gè)設(shè)置VIEWSTATE值的函數(shù),這里等下在選課構(gòu)造數(shù)據(jù)包的時(shí)候會(huì)講

模擬選課

隨便選一門課,然后提交,抓包,看一下有什么數(shù)據(jù)發(fā)送

 

前三個(gè)值可以在原網(wǎng)頁中input標(biāo)簽中找到,由于前兩項(xiàng)為空,就不獲取了,而第三項(xiàng)我們使用soup解析獲取即可,由于這個(gè)操作是每請(qǐng)求一次就變化的,我們寫成一個(gè)函數(shù),每次請(qǐng)求完成就設(shè)置一次。

 


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

而其他數(shù)據(jù),我們通過搜索響應(yīng)網(wǎng)頁就可以知道他們是干什么用的,這里我只說明我們要用的數(shù)據(jù)。

TextBox1為搜索框數(shù)據(jù),我們可以用這個(gè)來搜索課程,dpkcmcGrid:txtPageSize為一頁顯示多少數(shù)據(jù),經(jīng)過測試,服務(wù)器最多響應(yīng)200條。

值得注意的是ddl_xqbs這個(gè)校區(qū)數(shù)據(jù)信息,我所在的校區(qū)的數(shù)字代號(hào)為2,也許不同學(xué)校設(shè)置有所不同,需要自己設(shè)置一下,也可以從網(wǎng)頁中獲取

下面是基礎(chǔ)數(shù)據(jù)包,由于我們搜索課程與選擇課程都要使用這個(gè)基礎(chǔ)數(shù)據(jù)包,所以我們直接在init函數(shù)里面新增

  1. self.__base_data = { 
  2.     '__EVENTTARGET'''
  3.     '__EVENTARGUMENT'''
  4.     '__VIEWSTATE'''
  5.     'ddl_kcxz'''
  6.     'ddl_ywyl'''
  7.     'ddl_kcgs'''
  8.     'ddl_xqbs''2'
  9.     'ddl_sksj'''
  10.     'TextBox1'''
  11.     'dpkcmcGrid:txtChoosePage''1'
  12.     'dpkcmcGrid:txtPageSize''200'

然后我們關(guān)注一下這條數(shù)據(jù),我們搜索一下,發(fā)現(xiàn)這是課程的提交選課的代碼,所以我們也可以直接從網(wǎng)頁中獲取,而on表示選項(xiàng)被選上

 

  1. kcmcGrid:_ctl2:xk:'on' 

搜索課程

課程有很多信息,比如名字,上課時(shí)間,地點(diǎn),這些東西確定好了才知道選的是哪門課,所以我們先新建一個(gè)類來存儲(chǔ)信息

  1. class Lesson: 
  2.     def __init__(self, name, code, teacher_name, Time, number): 
  3.         self.name = name 
  4.         self.code = code 
  5.         self.teacher_name = teacher_name 
  6.         self.time = Time 
  7.         self.number = number 
  8.     def show(self): 
  9.         print('name:' + self.name + 'code:' + self.code + 'teacher_name:' + self.teacher_name + 'time:' + self.time

有了這個(gè)類,我們就可以進(jìn)行搜索課程了,具體代碼看下面代碼,解析網(wǎng)頁內(nèi)容就不細(xì)講了。

  1. def __search_lessons(self, lesson_name=''): 
  2.     self.__base_data['TextBox1'] = lesson_name.encode('gb2312'
  3.     request = requests.post(self.__headers['Referer'], data=self.__base_data, headers=self.__headers) 
  4.     soup = BeautifulSoup(request.text, 'lxml'
  5.     self.__set__VIEWSTATE(soup) 
  6.     return self.__get_lessons(soup) 
  7. def __get_lessons(self, soup): 
  8.     lesson_list = [] 
  9.     lessons_tag = soup.find('table', id='kcmcGrid'
  10.     lesson_tag_list = lessons_tag.find_all('tr')[1:] 
  11.     for lesson_tag in lesson_tag_list: 
  12.         td_list = lesson_tag.find_all('td'
  13.         code = td_list[0].input['name'
  14.         name = td_list[1].string 
  15.         teacher_name = td_list[3].string 
  16.         Time = td_list[4]['title'
  17.         number = td_list[10].string 
  18.         lesson = self.Lesson(name, code, teacher_name, Time, number) 
  19.         lesson_list.append(lesson) 
  20.     return lesson_list 

進(jìn)行選課

選課我們只要將lesson_list傳入即可,這就是我們之前創(chuàng)建的Lesson類的實(shí)例的列表,’Button’的內(nèi)容為’ 提交 ‘,這兩邊各有一個(gè)空格,完事后我們可以進(jìn)行發(fā)送請(qǐng)求進(jìn)行選課。

這里我們用正則提取了錯(cuò)誤信息,比如選課時(shí)間未到、上課時(shí)間沖突這些錯(cuò)誤信息來提示用戶,我們還解析了網(wǎng)頁的已選課程,這里也不細(xì)講了,都是基礎(chǔ)的網(wǎng)頁解析。

  1. def __select_lesson(self, lesson_list): 
  2.     data = copy.deepcopy(self.__base_data) 
  3.     data['Button1'] = '  提交  '.encode('gb2312'
  4.     for lesson in lesson_list: 
  5.         code = lesson.code 
  6.         data[code] = 'on' 
  7.     request = requests.post(self.__headers['Referer'], data=data, headers=self.__headers) 
  8.     soup = BeautifulSoup(request.text, 'lxml'
  9.     self.__set__VIEWSTATE(soup) 
  10.     error_tag = soup.html.head.script 
  11.     if not error_tag is None: 
  12.         error_tag_text = error_tag.string 
  13.         r = "alert\('(.+?)'\);" 
  14.         for s in re.findall(r, error_tag_text): 
  15.             print(s) 
  16.     print('已選課程:'
  17.     selected_lessons_pre_tag = soup.find('legend', text='已選課程'
  18.     selected_lessons_tag = selected_lessons_pre_tag.next_sibling 
  19.     tr_list = selected_lessons_tag.find_all('tr')[1:] 
  20.     for tr in tr_list: 
  21.         td = tr.find('td'
  22.         print(td.string) 

總結(jié)

這次我們完成了模擬正方教務(wù)系統(tǒng)選課的過程,由于這個(gè)教務(wù)系統(tǒng)技術(shù)比較陳舊,所以比較好弄,事實(shí)上搶課的時(shí)候用Fiddler即可完成操作,因?yàn)槲覀冎恍枰崆暗卿浫缓笥涗浘W(wǎng)址即可。

本文代碼Github地址:https://github.com/vhyz/ZF_Spider

責(zé)任編輯:未麗燕 來源: whyz's Blog
相關(guān)推薦

2024-01-12 18:26:44

2023-03-09 08:12:08

免登錄實(shí)Python腳本

2021-08-02 12:29:15

Python爬蟲網(wǎng)站

2020-08-19 17:14:26

Python數(shù)據(jù)函數(shù)

2018-01-02 09:56:04

Python12306火車票

2020-02-11 16:10:44

Redis分布式鎖Java

2012-08-21 11:26:17

Winform

2025-04-02 07:22:19

2023-07-02 14:05:13

2021-01-06 10:09:05

Spring Boothttps sslhttps

2021-05-25 10:05:39

Python模擬導(dǎo)彈代碼

2016-09-12 14:05:27

PythonPython解釋器Web

2020-02-24 10:29:24

數(shù)據(jù)庫系統(tǒng)肺炎

2020-12-18 08:55:20

Python火車票代碼

2022-02-25 14:04:56

TS前端代碼

2019-01-09 09:35:41

搶票Python軟件

2024-06-19 10:48:31

ChatGPTGPT項(xiàng)目

2019-01-24 09:00:00

PythonAutoML機(jī)器學(xué)習(xí)

2021-07-29 13:06:29

Python機(jī)器學(xué)習(xí)編程語言

2015-06-30 15:14:54

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)