python爬取教務系統
其實,自己是一直打算爬取教務系統的,可是懶啊懶,懶啊懶,就一直懶到現在了。所以,寒假開始,痛改前非,重新做人,就稍微接觸了一些python的知識,用來爬取每個學生都夢寐以求的教務系統。
前期準備
教務系統頁面解析
首先,我們看看,教務系統頁面長啥樣?
這裡,我們需要注意兩點:
- 驗證碼的獲取。
- 登陸時提交的資訊。
驗證碼獲取
這裡,我們只需要進行抓包即可,這裡我選擇使用Fiddler進行抓包。首先開啟Fiddler,然後重新整理一下教務系統的介面即可。
我們在這裡能夠看到,獲取驗證碼的URL是:http://210.42.121.241/servlet/GenImg
所以,我們就可以這樣一步一步來獲取我們需要的資訊。
- 提交請求到:http://210.42.121.241/servlet/GenImg
- 下載驗證碼到本地,進行讀取。(因為我也是剛接觸python,所以這裡就下載下來手動讀取了,後面學習過程中,我會嘗試用機器進行讀取的)
- 輸入驗證碼,提交到登陸請求,儲存cookies。(這一步是很重要的)
登陸時提交資訊
怎麼取提交道路請求呢,我們繼續來抓包。先清空Fiddler中的資訊,然後在網頁上輸入學號,密碼,驗證碼,點選登陸,就可以看到抓包資訊了。
http://210.42.121.241/servlet/Login
提交的請求包括三個引數:
{ id: 學號 pwd: 密碼 xdvfb: 驗證碼 }
這樣,我們就可以模擬取提交請求,並登陸到我們的教務系統啦~
模擬登陸
好,那麼我們現在就用python開始模擬登陸。
def get_cookie():
# 驗證碼地址和登陸地址
base_url = "http://210.42.121.241/servlet/GenImg"
login_url = "http://210.42.121.241/servlet/Login"
# 將cookie繫結到一個opener,cookie由cookielib自動管理
cookie = cookielib.MozillaCookieJar("file.txt")
handler = urllib2.HTTPCookieProcessor(cookie)
opener = urllib2.build_opener(handler)
# 使用者名稱和密碼
username = "xxxxxxxxxxxxxxx"
password = "xxxxxxxxxxxxxxx"
# 用opener訪問驗證碼地址,獲取驗證碼
picture = opener.open(base_url).read()
# 儲存驗證碼到本地
local = open("image.jpg", "wb")
local.write(picture)
local.close()
# 輸入驗證碼,並構造表單
secret_code = raw_input("請輸入驗證碼:")
data = {
'id': 'xxxxxxxxxxxx',
'pwd': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'xdvfb': secret_code
}
# 根據抓包資訊,構造headers
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Content-Length': 64,
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
}
# 獲取資料,儲存cooki,最後返回openner
data = urllib.urlencode(data)
request = urllib2.Request(login_url, data, headers)
response = opener.open(request)
cookie.save(ignore_discard=True, ignore_expires=True)
return opener
終於好啦,現在我們的驗證碼圖片就儲存到image.jpg裡了,我們的cookie就儲存到file.txt中了。
後面,我們就可以直接通過這個opener進行訪問啦~~~~
分析課表頁面
我們在選課頁面,單擊第二頁,通過抓包,我們可以看到訪問頁面。
好,通過抓包,我們可以看到:請求頁面的URL是:
http://210.42.121.241/stu/choose_PubLsn_list.jsp?XiaoQu=0&credit=0&keyword=&pageNum=2
引數說明:
- XiaoQu:這個引數很好理解,就是我們的校區。0表示全校,1表示1區(文理學部),2表示2區(工學部),3表示3區(資訊學部),4表示4區(醫學部)。
- credit:學分
- keyword:這個是查詢的關鍵字,這裡我們不需要使用,大家有想繼續深入的,可以繼續研究
- pageNum:頁碼。
好,獲取課程資訊的URL我們也解析出來了,現在就需要我們依次爬取每個頁面的資訊,獲取到課程資訊就行了。
爬取頁面
首先,這裡,因為總共有24頁,所以這裡,我打算採用多執行緒進行處理,將所有爬到的課程資訊儲存到一個lesson_list中,然後將這個list中的資訊儲存到檔案中即可。
構造Lesson實體類
class Lesson:
def __init__(self, name, degree, number, teacher, jobTitle, college, textbook, year, semester, time, remarks):
self.name = name
self.degree = degree
self.number = number
self.teacher = teacher
self.jobTitle = jobTitle
self.college = college
self.textbook = textbook
self.year = year
self.semester = semester
self.time = time
self.remarks = remarks
def get_name(self):
return self.name
def get_degree(self):
return self.degree
def get_number(self):
return self.number
def get_teacher(self):
return self.teacher
def get_job_title(self):
return self.jobTitle
def get_college(self):
return self.college
def get_textbook(self):
return self.textbook
def get_year(self):
return self.year
def get_semester(self):
return self.semester
def get_time(self):
return self.time
def get_remarks(self):
return self.remarks
def __str__(self):
return str(self.name + "\t" + self.degree + "\t" + self.number + "\t" + self.teacher + "\t" + self.jobTitle + "\t" + self.college + "\t" + self.textbook + "\t" + self.year + "\t" + self.semester + "\t" + self.time + "\t" + self.remarks)
構造執行緒類MyThread
- 執行緒類MyThread繼承threading.Thread
- 屬性說明:
- name:執行緒名稱
- page:該執行緒要爬取的頁碼
- lesson_list:爬取的課程資訊儲存到lesson_list中
- opener:opener
# -*- coding: utf-8 -*-
import sys
import threading
import urllib2
from bs4 import BeautifulSoup
from Utils import get_lesson
reload(sys)
sys.setdefaultencoding('utf-8')
threadLock = threading.Lock()
class MyThread(threading.Thread):
def __init__(self, name, page, lesson_list, opener):
threading.Thread.__init__(self)
self.name = name
self.page = page
self.lesson_list = lesson_list
self.opener = opener
def run(self):
get_lessons(self.name, self.page, self.lesson_list, self.opener)
def get_lessons(name, page, lesson_list, opener):
print name + " is starting......"
try:
lesson_url = "http://210.42.121.241/stu/choose_PubLsn_list.jsp?XiaoQu=0&credit=0&keyword=&pageNum=" + page
result = opener.open(lesson_url).read().decode('gb2312', 'ignore').encode('utf-8')
soup = BeautifulSoup(result, "html.parser")
trs = soup.find_all(name='tr')
threadLock.acquire() # 互斥地寫入lesson_list
for j in range(1, len(trs)):
tr = trs[j]
lesson = get_lesson(tr)
lesson_list.append(lesson)
threadLock.release()
except urllib2.HTTPError, e:
print e.code
構造工具類Util
這裡,需要明白一點,我們爬取地頁面中,課程資訊是儲存到一個table中,第一個tr是表頭,往後的每個tr都代表了一個lesson,所以我們在Util中定義一個方法get_lesson(tr)用來解析。
這裡,具體的解析,我寫的也很粗暴,詳細的我就不多說了,大家看一下原始碼就明白了。
# -*- coding: utf-8 -*-
import cookielib
import urllib
import urllib2
from bs4 import BeautifulSoup
from xlwt import Workbook
from Lesson import Lesson
def get_lesson(tr):
soup = BeautifulSoup(str(tr), "html.parser")
tds = soup.find_all(name='td')
name = tds[0].string
degree = tds[1].string
teacher = tds[3].string
jobTitle = tds[4].string
college = tds[5].string
textbook = tds[6].string
year = tds[7].string
semester = tds[8].string
soup = BeautifulSoup(str(tds[2]), "html.parser")
number = soup.findAll("font")[0].string + str(tds[2])[str(tds[2]).index("</font>") + 7: str(tds[2]).rindex("</td>")]
soup = BeautifulSoup(str(tds[9]), "html.parser")
time = soup.findAll(name="div")[0].string.strip().replace(" ", "").replace("\n", "")
soup = BeautifulSoup(str(tds[10]), "html.parser")
remarks = soup.findAll(name="div")[0].string.strip().replace(" ", "").replace("\n", "")
lesson = Lesson(name, degree, number, teacher, jobTitle, college, textbook, year, semester, time, remarks)
return lesson
def init(sheet):
sheet.write(0, 0, "課程名稱")
sheet.write(0, 1, "學分")
sheet.write(0, 2, "剩餘/最大人數")
sheet.write(0, 3, "教師名")
sheet.write(0, 4, "職稱")
sheet.write(0, 5, "授課學院")
sheet.write(0, 6, "教材")
sheet.write(0, 7, "學年")
sheet.write(0, 8, "學期")
sheet.write(0, 9, "上課時間地點")
sheet.write(0, 10, "備註")
def save_lesson(lesson, sheet, row):
sheet.write(row, 0, lesson.get_name())
sheet.write(row, 1, lesson.get_degree())
sheet.write(row, 2, lesson.get_number())
sheet.write(row, 3, lesson.get_teacher())
sheet.write(row, 4, lesson.get_job_title())
sheet.write(row, 5, lesson.get_college())
sheet.write(row, 6, lesson.get_textbook())
sheet.write(row, 7, lesson.get_year())
sheet.write(row, 8, lesson.get_semester())
sheet.write(row, 9, lesson.get_time())
sheet.write(row, 10, lesson.get_remarks())
def save(lesson_list):
book = Workbook(encoding='utf-8')
sheet = book.add_sheet("lesson")
init(sheet)
row = 1
for lesson in lesson_list:
save_lesson(lesson, sheet, row)
row = row + 1
book.save("lesson.xls")
def get_cookie():
# 驗證碼地址和post地址
base_url = "http://210.42.121.241/servlet/GenImg"
login_url = "http://210.42.121.241/servlet/Login"
# 將cookie繫結到一個opener,cookie由cookielib自動管理
cookie = cookielib.MozillaCookieJar("file.txt")
handler = urllib2.HTTPCookieProcessor(cookie)
opener = urllib2.build_opener(handler)
# 使用者名稱和密碼
username = "xxxxxxxxxxxxxxx"
password = "xxxxxxxxxxxxxxx"
# 用opener訪問驗證碼地址,獲取cookie
picture = opener.open(base_url).read()
# 儲存驗證碼到本地
local = open("image.jpg", "wb")
local.write(picture)
local.close()
# 輸入驗證碼,並構造表單
secret_code = raw_input("請輸入驗證碼:")
data = {
'id': 'xxxxxxxxxxxx',
'pwd': 'xxxxxxxxxxxxxxxx',
'xdvfb': secret_code
}
# 根據抓包資訊,構造headers
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Content-Length': 64,
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
}
data = urllib.urlencode(data)
request = urllib2.Request(login_url, data, headers)
response = opener.open(request)
cookie.save(ignore_discard=True, ignore_expires=True)
return opener
儲存到檔案
這裡,儲存到檔案,我們使用了python的一個包xlwt,就來將我們的檔案直接儲存到excel檔案中。
總結
我也就不多說什麼了,思路就是我寫的這樣。程式碼我都放在了我的github(專案地址)上,大家可以去下載學習,有什麼不好的地方,歡迎大家多批評指正。