其實之前有寫過一篇帖子了 舊帖地址(知乎)
在之前使用教務系統的過程中,偶然一次發現登上教務系統后再退出來的后的登錄網址竟然不需要驗證碼,想着之前有寫過教務系統的爬蟲模擬登錄,沒驗證碼的豈不是更好干(之前那次折騰了好久hhh,后面還是用selenium實現的成績爬取,相比直接爬取,selenium的性能還是比不上畢竟selenium本來就是用來搞自動化的hhh),然后今天閑着就重寫了相同的功能並實現了成績的爬取。
簡單寫一下思路:
- 百度直接搜索出來的教務系統網址是需要驗證碼的,登錄上去之后,再退出來就得到不需要驗證碼的登錄網址
-
點開我們可以發現是需要驗證碼的

-
登錄上去之后再退出,下圖箭頭所指:

-
退出之后,是不是神奇的發現驗證碼不見了(見下圖)哈哈哈,這就是我們要的。

-
- 正式開始我們的分析
- 要分析,肯定是得要看網頁源代碼滴(鼠標右擊查看網頁源代碼),當然還有F12提供的開發者調試工具


2.讓我們來看看網頁源代碼- 我們直接找到表單位置,可以注意到當點擊登陸時會觸發函數
submitForm1()==> 好嘞,這不好辦嗎?接下來就讓我們來順騰摸瓜

- 我們復制函數的名稱,並直接ctrl+F查找到函數的位置

- 仔細看看函數,不難發現函數取得到了表單中我們輸入的賬號密碼后都調用了
encodeInp()函數,然后組成了一個用變量encoded保存的字符串。

3.輸入一個錯誤的賬號密碼,點擊登錄看看開發者調試工具里面network的情況 - 我們可以注意到紅框中的那個post請求

- 點開后,我們可以看到
cookie,也不難發現encoded字樣,於是我們可以猜測提交的表單中就只有這一個數據,且是登錄驗證的唯一一個數據(比之前那個貼簡單太多了,自己流下了心酸的淚水)

- 接着分析,關掉瀏覽器,重新打開這個不要驗證碼的網址(重新獲得服務端分配的一個cookie),輸入跟剛剛錯誤的賬號密碼再看看那個
encoded有沒有變化,因為根據自己上次的經驗encoded是根據每次分配的cookie不同而變化的.....然后我們發現是沒有變化的!!!是沒有變化的!!!是沒有變化的!!!(重要的事情說三遍,這還不好辦嘛???)

- 我們直接找到表單位置,可以注意到當點擊登陸時會觸發函數
- 繼續順騰摸瓜(戰術性滑稽臉)
- 上面說到點擊網頁上的登錄按鈕時會觸發函數
submitForm1(),然后在這個函數中又會調用對賬號密碼進行加工(其實是加密hhh)的encodeInp()函數,我們來找到encodeInp()函數的位置,相同的操作CTRL+F,結果發現函數不在這兒。

- 既然函數不在該頁面的源代碼中,肯定是引進來的js文件,我們在html的頭部去找引入的文件,結果發現只引入了一個js文件,那么我們可以肯定
encodeInp()函數就在該文件中了。

- 在點開的
conwork.js文件中搜素encodeInp()函數,找到了!!!

- 上面說到點擊網頁上的登錄按鈕時會觸發函數
- 仔細看看
conwork.js文件- 看了好一會兒后,發現自己根本看不懂,想着反正是調用的這個文件中的函數,那不如自己去改改代碼調用
encodeInp()函數,看能不能調用成功,輸出跟調試工具中一樣的encoded,如果是一樣的我們目的就達到了,加密的過程就是在submitForm1()中調用encodeInp()函數,且跟cookie沒關系,也就是說每個人的賬號密碼最后經過加密出來的字符串每次都是固定的。

- 這里我就用了自己的賬號密碼,結果可以發現是一樣的。

- 看了好一會兒后,發現自己根本看不懂,想着反正是調用的這個文件中的函數,那不如自己去改改代碼調用
- 要分析,肯定是得要看網頁源代碼滴(鼠標右擊查看網頁源代碼),當然還有F12提供的開發者調試工具
- 接下來我們就可以寫python代碼了
- 我們先把
conwork.js文件的代碼和經過更改的且python的execjs庫能調用的submitForm1()函數放在一個同一個js文件里面(這里我把函數名稱換了一下,換成了encode)

- 寫代碼的思路我就不說了,代碼貼在下面了,不過這次我倒是驚奇的發現不加header都能直接登陸。
- 我們先把
import requests
import execjs
from bs4 import BeautifulSoup
class Login:
def __init__(self):
self.url = 'http://jiaowu2.hufe.edu.cn/jsxsd/'
header = {
"Content-Type": "text/html;charset=utf-8",
"Vary": "Accept-Encoding"
}
self.session = requests.session()
self.session.get(self.url,headers=header)
self.username = '****'#賬號
self.password = '****'#密碼
self.GetEncoded()
self.login()
def GetEncoded(self):#該函數獲取加密后的字符串
with open(r'conwork.js', encoding='utf-8') as f:
js = execjs.compile(f.read())
self.encoded = js.call('encode', self.username,self.password)
f.close()
return self.encoded
def login(self):
postData = {
'encoded': self.encoded.strip() # 賬號密碼加密后的東西
}
return self.session.post('http://jiaowu2.hufe.edu.cn/jsxsd/xk/LoginToXk',data=postData)
def GetScore(self):#爬取全部課程成績
response = self.session.get('http://jiaowu2.hufe.edu.cn/jsxsd/kscj/cjcx_list')
grade_html = response.text
soup = BeautifulSoup(grade_html, 'html.parser')
tr_lable = soup.find_all("tr")[1:]
all_rows = [] # 該列表存放每一個課程的詳細信息。
all_rows.append([i.text for i in soup.find_all("th")])
for tr in tr_lable:
tds = tr.find_all('td')
row = []
for td in tds:
row.append(td.text)
all_rows.append(row)
return all_rows
test = Login()
test.login()
print(test.GetScore())#打印爬取的成績
最后,代碼肯定有很多寫的不夠好的地方,望大家多多包含。嘿嘿嘿~
