0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Python也有這么強(qiáng)大的用處哦!用Python腳本自助搶課

馬哥Linux運(yùn)維 ? 來源:未知 ? 作者:李倩 ? 2018-07-06 11:20 ? 次閱讀

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

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

嘗試登錄

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

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

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

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

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

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

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

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

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

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

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

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

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

classSpider:

def __init__(self, url):pp

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即為帶有會話信息的網(wǎng)址,保存的url格式為

your_ip/(bdq1aj45lpd42o55vqpfgpie)/

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

獲取會話信息(使用Cookie)

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

def get_cookie():

request = requests.get('http://xxx.xxx.xxx.xxx') #以某教務(wù)系統(tǒng)為例子

cookie = requets.cookie

return cookie

而requests中使用Cookie很簡單

只需要這樣

def use_cookie(cookie):

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

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

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

驗(yàn)證碼的處理

分析r返回的文本信息

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

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模塊,可以實(shí)現(xiàn)自動打開圖片

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

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

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

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

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

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

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

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

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

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

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': '學(xué)生'.encode('gb2312'),

'Button1': '',

'lbLanguage': '',

'hidPdrs': '',

'hidsc': '',

}

return data

登錄

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

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

def login(self,uid,password):

whileTrue:

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:

returnTrue

獲取選課信息

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

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

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

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)

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

模擬選課

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

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

def __set__VIEWSTATE(self, soup):

__VIEWSTATE_tag = soup.find('input', attrs={'name': '__VIEWSTATE'})

self.__base_data['__VIEWSTATE'] = __VIEWSTATE_tag['value']

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

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

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

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

self.__base_data = {

'__EVENTTARGET': '',

'__EVENTARGUMENT': '',

'__VIEWSTATE': '',

'ddl_kcxz': '',

'ddl_ywyl': '',

'ddl_kcgs': '',

'ddl_xqbs': '2',

'ddl_sksj': '',

'TextBox1': '',

'dpkcmcGrid:txtChoosePage': '1',

'dpkcmcGrid:txtPageSize': '200',

}

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

kcmcGrid:_ctl2:xk:'on'

搜索課程

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

classLesson:

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)

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

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)

returnself.__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

進(jìn)行選課

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

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

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

ifnot error_tag isNone:

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)

總結(jié)

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

由于不同學(xué)校的正方教務(wù)系統(tǒng)有可能不同,所以上面很多細(xì)節(jié)都是需要修改的。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • python
    +關(guān)注

    關(guān)注

    53

    文章

    4753

    瀏覽量

    84069
  • 腳本
    +關(guān)注

    關(guān)注

    1

    文章

    382

    瀏覽量

    14760

原文標(biāo)題:用 Python 搞定正方教務(wù)系統(tǒng)之搶課篇

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    基于Python腳本的R語言的函數(shù)

    本文介紹了采用創(chuàng)建一個Python腳本,腳本模仿R風(fēng)格的函數(shù)的方法來方便地進(jìn)行統(tǒng)計(jì)。 是R語言還是
    的頭像 發(fā)表于 10-12 09:33 ?2000次閱讀
    基于<b class='flag-5'>Python</b><b class='flag-5'>腳本</b>的R語言的函數(shù)

    Python能做什么?自學(xué)Python獲得人生的第一桶金經(jīng)驗(yàn)分享

    Python能做什么?Python是什么玩意?有這么神?偷偷搜了一下,我卻嚇了一跳。早就有一大批人,專門Python搞各種騷操作。論壇里到
    的頭像 發(fā)表于 01-04 17:35 ?3639次閱讀

    10個殺手級的Python自動化腳本

    今天浩道跟大家分享10個日常工作中用到的python自動化腳本。讓你感受一番python簡單強(qiáng)大之處!
    發(fā)表于 11-28 11:07 ?637次閱讀

    Python這么火,那有啥優(yōu)勢呢?

    創(chuàng)建功能強(qiáng)大且整潔的應(yīng)用程序。 而Python則不能自夸擁有如此多的框架。的最多的是Django和Flask。 但是,我們可以向你保證,由于Python社區(qū)的不斷發(fā)展,這種局面很難快
    發(fā)表于 05-09 13:30

    Python與C#對比

    !Python和C#語言都是功能強(qiáng)大的有前途的編程語言,那么兩者有什么區(qū)別呢?1. Python原本就被設(shè)計(jì)成類似英語表達(dá)一樣,所以語法簡單,沒有像句法括號和大量的修飾詞,易讀易學(xué);
    發(fā)表于 05-14 17:14

    python寫一個建模的腳本-微帶天線的模型建立與仿真的過程搞定了

    大多數(shù)聯(lián)合仿真的腳本都是使用的Matlab進(jìn)行編程,網(wǎng)上也有不少現(xiàn)成的api,因?yàn)閷?b class='flag-5'>python比較熟悉,且python除了數(shù)值計(jì)算其他的功能也相當(dāng)
    發(fā)表于 02-19 16:51

    如何在 IIS 中執(zhí)行 Python 腳本

    如何在 IIS 中執(zhí)行 Python 腳本 Python 是一種解釋腳本語言,概念類似 Microsoft Visual Basic Scripting Edition (VBSc
    發(fā)表于 02-23 15:13 ?1219次閱讀

    matlab如何調(diào)用python腳本文件,路徑是怎樣的

    Python是純粹的自由軟件,源代碼和解釋器CPython遵循GPL協(xié)議。Python語法簡潔清晰,特色之一是強(qiáng)制空白符作為語句縮進(jìn)。Python具有豐富和
    發(fā)表于 12-04 14:52 ?7986次閱讀

    如何使html網(wǎng)頁與python腳本進(jìn)行通信

    現(xiàn)在運(yùn)行python腳本并轉(zhuǎn)到http://localhost/或http://localhost/并開始按下按鈕,您應(yīng)該會在python腳本上看到輸入。
    的頭像 發(fā)表于 11-04 10:12 ?7771次閱讀

    如何使用Python和HFSS的聯(lián)合仿真實(shí)現(xiàn)微帶天線的設(shè)計(jì)

    大多數(shù)聯(lián)合仿真的腳本都是使用的Matlab進(jìn)行編程,網(wǎng)上也有不少現(xiàn)成的api,因?yàn)閷?b class='flag-5'>python比較熟悉,且python除了數(shù)值計(jì)算其他的功能也相當(dāng)
    發(fā)表于 11-17 10:31 ?1次下載
    如何使用<b class='flag-5'>Python</b>和HFSS的聯(lián)合仿真實(shí)現(xiàn)微帶天線的設(shè)計(jì)

    Python也太強(qiáng)大了吧

    ? ? ? ? 你是不是也有過這樣無語的時(shí)刻: ? 蹲點(diǎn)東西,手指瘋狂點(diǎn)擊,可半秒鐘不到就沒了!難道對面是無影手嗎? ? 其實(shí)你可以Python寫個小程序,在一個網(wǎng)站上一秒內(nèi)發(fā)起上
    的頭像 發(fā)表于 12-23 16:09 ?1538次閱讀

    Python】如何將Python腳本打包成exe可執(zhí)行文件

    Python實(shí)用技巧】如何將Python腳本打包成exe可執(zhí)行文件?
    的頭像 發(fā)表于 08-18 12:40 ?1.8w次閱讀
    【<b class='flag-5'>Python</b>】如何將<b class='flag-5'>Python</b><b class='flag-5'>腳本</b>打包成exe可執(zhí)行文件

    Python怎么玩轉(zhuǎn)JS腳本

    本項(xiàng)目旨在讓大家了解如何用Python來執(zhí)行JS腳本,其主要目的是在進(jìn)行數(shù)據(jù) 分析時(shí),需要利用爬蟲獲取數(shù)據(jù),有時(shí)會遇到JS混淆加密反爬取難點(diǎn),此時(shí)我們需 要獲取網(wǎng)頁JS加密代碼將其轉(zhuǎn)換為Python代碼運(yùn)行,從而破解JS加密
    的頭像 發(fā)表于 02-23 16:26 ?931次閱讀
    <b class='flag-5'>Python</b>怎么玩轉(zhuǎn)JS<b class='flag-5'>腳本</b>

    使用Python腳本實(shí)現(xiàn)自動化運(yùn)維任務(wù)

    許多運(yùn)維工程師會使用 Python 腳本來自動化運(yùn)維任務(wù)。Python 是一種流行的編程語言,具有豐富的第三方庫和強(qiáng)大的自動化能力,適用于許多不同的領(lǐng)域。
    的頭像 發(fā)表于 04-08 10:36 ?1520次閱讀

    python寫驗(yàn)證環(huán)境cocotb

    本文介紹了cocotb的安裝、python tb文件的寫法、xrun仿真cocotb的腳本等,我們來看看體驗(yàn)如何。
    的頭像 發(fā)表于 07-24 09:38 ?293次閱讀
    <b class='flag-5'>用</b><b class='flag-5'>python</b>寫驗(yàn)證環(huán)境cocotb