python 爬取 強智科技教務系統(湖南)

扯閒:

   說在前面的廢話: 又開學了,我要好好學習,想找個空教室自習真不容易(雖然有書院,可是人多啊,找個沒人的教室自習多好~),一樓一樓的往上找教室,就算我不以爲麻煩,可是小姐姐(基友)也會以爲麻煩),因此( 攤手)。html

進入正題:

   想到兩種方法:python

1:先經過谷歌抓包,得到全部校區教學樓上課的課表,而後本身寫個小程序自動判斷下,而後彙總。缺點若是教務系統更新了,原來的就不能用了(課表都變了,你還咋用233333)web

2:仍是先分析各個數據包,而後模擬登陸教務系統(非pyautogui,而是使用session模擬瀏覽器),而後發送特定數據包,得到課表,再統計輸出,實時得到最新信息(若是教務系統上出錯,那我也無奈啊)小程序

爲了讓小姐姐(基友)有更好的使用體驗,確定選擇第二種啦~瀏覽器

1、使用帳號密碼登陸教務系統(以湖南工商大學教務系統爲例)

  百度直接搜到的網址須要驗證碼,可是有個不須要驗證碼的(這就很爽了)點擊這裏進入登陸服務器

 一、 分析登陸網頁cookie

   先打開F12 點擊Network,先本身登陸走一遍,發現它直接跳轉到了教務系統裏面,session

咱們找到LoginToXk這個數據包,注意框框裏面的是加密後的數據,也就是咱們須要讓服務器認證的東西。app

固然這個數據是(帳號密碼)被加密後再發送的,因此咱們再退到登陸頁面。函數


 

    進入登陸頁面後,點擊右鍵,檢查。

    咱們發現咱們點了登陸那個按鈕後,會先通過一個JS處理,就是submitForm1()這個,而後把加密後的東西 傳送給咱們上面找到的LoginToXk這個數據包。


  把這個代碼展開,咱們找到這個方法,再最下面。

大概看下代碼,加密在這兩個地方

encodeInp(xh);

encodeInp(pwd);

咱們經過瀏覽器自帶的search 查找,發現這個加密函數在另一個JS裏面,順藤摸瓜。

 



 

從新看下登陸界面的數據包,找到conwork.js 咱們看下如如何加密的。

 


恩,有點麻煩,怎麼辦。。。。

模擬加密,寫了開頭,既然它有加密的JS,我爲啥不直接用?我用的是python!問下度年,發現 

import execjs  

能夠直接調用本地的js 而後得到返回的信息,nice! 咱們先把這個JS保存在本地。

而後寫個調用js加密,而後返回結果的函數,注意顯示帳號加密,而後中間有三個%,而後再接密碼加密。

import execjs def get_js(self, msg): # python 調用JS加密 返回 加密後的結果 with open('conwork.js', encoding='utf-8') as f: js = execjs.compile(f.read()) return js.call('encodeInp', msg) encode = str(self.get_js(account)) + "%%%" + str(self.get_js(psw)) + "=" # 得到加密後的東西

加密搞定。

而後咱們模擬LoginToXk,發送數據包,這裏咱們須要有個cookies,cookies並且這個cookies 在全局都有用。

cookie在進入登陸頁面就會自動給你分配。因此咱們先得到cookies.

# -*- conding:utf-8 -*- import requests import re import execjs ses = requests.session() def get_login_cookies(): header = { "Content-Type": "text/html;charset=GBK", "Vary": "Accept-Encoding" } url = "http://jwgl.hnuc.edu.cn/jsxsd/" ses.get(url=url, headers=header, timeout=1000) # ses已經得到了cookies cookies = ses.cookies.get_dict() # 得到臨時的cookies cookies = str(cookies).replace("{", '').replace("'", '').replace(":", '=').replace('}', '').replace(",", ";") cookies = cookies.replace(" ", '') return cookies

而後發送咱們驗證

def login(cookies, jsmsg): header = { "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;" "q=0.8,application/signed-exchange;v=b3", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9", "Cache-Control": "max-age=0", "Content-Length": "47", "Content-Type": "application/x-www-form-urlencoded", # 接收類型 "Cookie": cookies, "Host": "jwgl.hnuc.edu.cn", "Origin": "http://jwgl.hnuc.edu.cn", "Proxy-Connection": "keep-alive", "Referer": "http://jwgl.hnuc.edu.cn/jsxsd/", "Upgrade-Insecure-Requests": "1", "User-Agent":"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/76.0.3809.132 Mobile Safari/537.36", } PostData = { 'encoded': jsmsg # 帳號密碼加密後的東西 } url = 'http://jwgl.hnuc.edu.cn/jsxsd/xk/LoginToXk' msg = ses.post(url, headers=header, data=PostData, timeout=1000).text # 這個跳轉 # print("cookies驗證:"+str(msg))

這樣咱們的cookies 就能夠經過教務系統的登陸驗證了

2、教室課表查詢和過濾

  找到教室課表查詢,這兩個參數必須選擇。


點擊查詢,教務系統比較爛,相應的慢,233333.

找到這個/kbxx_classroom_ifr數據包,再看下Preview信息。



沒錯就是表格形式的。

既然是表格信息,咱們直接保存爲csv格式,而後在處理。

緣由:教務系統響應太慢,對小姐姐(基友)不友好,其次,表格我就更新一次,確保偏差在可控範圍。

表格處理咱們是用pandas 這個庫就行了

import pandas as pd

得到教室信息代碼保存爲csv代碼:

def get_all_room(self, where, what): # 得到教室信息 week = get_now_week() beg = week end = week url = "http://jwgl.hnuc.edu.cn/jsxsd/kbcx/kbxx_classroom_ifr" data = { "xnxqh": "2019-2020-1", # 時間不變 "skyx": "", "xqid": where, # 哪一個校區 "jzwid": what, # 哪一個教學樓 "classroomID": "", "jx0601id": "", "jx0601mc": "", "zc1": beg, "zc2": end, "jc1": "", "jc2": "", } msg = ses.post(url, data=data).text msg = pd.read_html(msg, encoding="UTF-8", header=1)[0] # 第幾個表格 pd.set_option('display.width', None) pd.set_option('display.unicode.east_asian_width', True) # 寬度對齊 msg.to_csv(r''+str(where)+str(what)+'.csv', mode='w+', encoding='utf_8_sig', header=1, index=0) print(str(where)+str(what)+"csv文件保存成功!\n")

而後就是簡單空教室判斷和查詢了,一個一個的查看是否爲空,是的話記錄教室,不然跳過。

時間緣由就很少闡述了,我封裝成了一個類,你們能夠直接調用,注意記得把加密的JS文件放在目錄下,或者你寫絕對路徑。

# -*- conding:utf-8 -*- import requests import re import execjs from bs4 import BeautifulSoup import pandas as pd import csv from datetime import datetime """ @author:ym @time:09-2019/9/2 """ class School: """ :login_in(user,psw) # 登陸到教務系統,必須先進行這這步 :get_xskb_lis() # 返回登陸者本人的課表 返回的是元組數據,s[0] 去掉 :day_init() # 天天更新教室狀況 使用定時更新 :get_msg_by_csv() # 返回教室狀況 每一節大課 一個\n 字符串型 :get_cj() # 返回登陸者的成績 ,獲得後使用’\n'分割而後分塊輸出 字符串型 """ def __init__(self): self.ses = requests.session() def get_js(self, msg): # python 調用JS加密 返回 加密後的結果 with open('conwork.js', encoding='utf-8') as f: js = execjs.compile(f.read()) return js.call('encodeInp', msg) def get_login_cookies(self): header = { "Content-Type": "text/html;charset=GBK", "Vary": "Accept-Encoding" } url = "http://jwgl.hnuc.edu.cn/jsxsd/" self.ses.get(url=url, headers=header, timeout=1000) cookies = self.ses.cookies.get_dict() # 得到臨時的cookies cookies = str(cookies).replace("{", '').replace("'", '').replace(":", '=').replace('}', '').replace(",", ";") cookies = cookies.replace(" ", '') return cookies def login(self, cookies, jsmsg): header = { "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;" "q=0.8,application/signed-exchange;v=b3", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9", "Cache-Control": "max-age=0", "Content-Length": "47", "Content-Type": "application/x-www-form-urlencoded", # 接收類型 "Cookie": cookies, "Host": "jwgl.hnuc.edu.cn", "Origin": "http://jwgl.hnuc.edu.cn", "Proxy-Connection": "keep-alive", "Referer": "http://jwgl.hnuc.edu.cn/jsxsd/", "Upgrade-Insecure-Requests": "1", "User-Agent":"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/76.0.3809.132 Mobile Safari/537.36", } PostData = { 'encoded': jsmsg # 帳號密碼加密後的東西 } url = 'http://jwgl.hnuc.edu.cn/jsxsd/xk/LoginToXk' msg = self.ses.post(url, headers=header, data=PostData, timeout=1000).text # 這個跳轉 # print("cookies驗證:"+str(msg)) def login_in(self, account, psw): # 輸入帳號密碼,讓cookies 生效 jsmsg = str(self.get_js(account)) + "%%%" + str(self.get_js(psw)) + "=" # 得到加密後的東西 self.get_login_cookies() # cookies 初始化 self.login(account, jsmsg) # def get_now_week(self): now = "2019-09-02 00:03:00" # 第一週 now = datetime.strptime(now, '%Y-%m-%d %H:%M:%S') # 第一週 end = datetime.now() week = int((end - now).days / 7) + 1 # return week def get_all_room(self, where, what): # 得到教室信息 week = self.get_now_week() beg = week end = week url = "http://jwgl.hnuc.edu.cn/jsxsd/kbcx/kbxx_classroom_ifr" data = { "xnxqh": "2019-2020-1", # 時間不變 "skyx": "", "xqid": where, # 哪一個校區 "jzwid": what, # 哪一個教學樓 "classroomID": "", "jx0601id": "", "jx0601mc": "", "zc1": beg, "zc2": end, "jc1": "", "jc2": "", } msg = self.ses.post(url, data=data).text msg = pd.read_html(msg, encoding="UTF-8", header=1)[0] # 第幾個表格 pd.set_option('display.width', None) pd.set_option('display.unicode.east_asian_width', True) # 寬度對齊 msg.to_csv(r''+str(where)+str(what)+'.csv', mode='w+', encoding='utf_8_sig', header=1, index=0) print(str(where)+str(what)+"csv文件保存成功!\n") def get_msg_by_csv_pre(self, today, classs, where, what): filname = str(where)+str(what)+".csv" ans = "" with open(file=filname, encoding="UTF-8") as f: f_csv = csv.reader(f) temp = 1 for i in f_csv: # 遍歷每一行 if temp == 1: # 第一行自動跳過 temp += 1 continue # print(i[6*(today-1)+classs]) if i[6*(today-1)+classs] in (None, ""): # 是空 ans += i[0] + " " return ans + "\n" # 返回全部的,這天這節課沒有課的教室 def exchange(self, where, what): temp = [] if where == 1: temp.append("00001") # 南院 if int(what) in range(1, 4): # 左開右閉 temp.append("0000"+str(what)) return temp else: return None elif where == 2: temp.append("00002") # 北院 temp.append("332328065C2440CBAC97F4A714E8937F") return temp return None def get_msg_by_csv(self, where, what): temp = self.exchange(where, what) if temp in (None, ""): return None where = temp[0] what = temp[1] d = datetime.today() # 獲取當前日期時間 today = d.isoweekday() # 得到當前的星期 week = str(self.get_now_week()) ans = "本學期第"+week+"周星期" + str(today) + "" if where == "00001": ans += "(南校區," if what == '00001': ans += "一教)" elif what == '00002': ans += "二教)" else: ans += "三教)" else: ans += "(北校區,教學樓)" ans += "空閒教室以下:\n" for i in range(1, 5): ans += "第"+str(i*2-1)+"-"+str(i*2)+"節課:" ans += self.get_msg_by_csv_pre(today, i, where, what) return ans def day_init(self): # 天天 自動初始化 try: try: self.get_all_room("00001", "00001") # 南院一教 except: pass try: self.get_all_room("00001", "00002") # 南院二教 except:pass try: self.get_all_room("00001", "00003") # 南院三教 except: pass try: self.get_all_room("00002", "332328065C2440CBAC97F4A714E8937F") # 北院教學樓 except: pass return True except: return False if __name__ == '__main__': a = School() a.login_in("學號", "密碼") msg = a.get_msg_by_csv(1, 1) print(msg) 

效果圖:


get_msg_by_csv(1, 1)
後面的兩個參數,第一個數字表示是南院仍是北院(北院是2) 第二個數字表示教學樓.如查看南院一教 就是 1 1

有問題能夠在下面留言,該功能整合到QQ機器人,直接經過QQ查詢教務系統。nice!同時還有我的成績查詢和我的課表查詢。

相關文章
相關標籤/搜索