1. 程式人生 > >python爬取教務系統

python爬取教務系統

其實,自己是一直打算爬取教務系統的,可是懶啊懶,懶啊懶,就一直懶到現在了。所以,寒假開始,痛改前非,重新做人,就稍微接觸了一些python的知識,用來爬取每個學生都夢寐以求的教務系統。

前期準備

教務系統頁面解析

首先,我們看看,教務系統頁面長啥樣?

這裡,我們需要注意兩點:

  1. 驗證碼的獲取。
  2. 登陸時提交的資訊。

驗證碼獲取

這裡,我們只需要進行抓包即可,這裡我選擇使用Fiddler進行抓包。首先開啟Fiddler,然後重新整理一下教務系統的介面即可。

我們在這裡能夠看到,獲取驗證碼的URL是:http://210.42.121.241/servlet/GenImg

所以,我們就可以這樣一步一步來獲取我們需要的資訊。

  1. 提交請求到:http://210.42.121.241/servlet/GenImg
  2. 下載驗證碼到本地,進行讀取。(因為我也是剛接觸python,所以這裡就下載下來手動讀取了,後面學習過程中,我會嘗試用機器進行讀取的)
  3. 輸入驗證碼,提交到登陸請求,儲存cookies。(這一步是很重要的)

登陸時提交資訊

怎麼取提交道路請求呢,我們繼續來抓包。先清空Fiddler中的資訊,然後在網頁上輸入學號,密碼,驗證碼,點選登陸,就可以看到抓包資訊了。

  1. http://210.42.121.241/servlet/Login

    是登陸的URL。

  2. 提交的請求包括三個引數:

    {
        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

  1. 執行緒類MyThread繼承threading.Thread
  2. 屬性說明:
    • 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(專案地址)上,大家可以去下載學習,有什麼不好的地方,歡迎大家多批評指正。