python爬蟲學習(4) —— 手刃「URP教務系統」


0. 本爬蟲目標

  • 模擬登陸URP教務系統
  • 查詢 本學期/歷年 成績
  • 計算歷年成績的績點

下面是一點廢「私」話「貨」:
一般情況,查詢成績大家會通過如下方式:

登陸信息門戶 -> 轉到教學空間 -> 選擇教務管理 -> 選擇綜合查詢  

最終可以看到你的成績
吐槽一下,查詢成績必須使用IE內核的瀏覽器,在IE11中還需要設置兼容性,非IE內核的瀏覽器是無法查看成績的。

CR1

好。我們查看一下源代碼,或者憑經驗可以發現,,這個「成績」是嵌套在一個frame框架中的。
啊,,好蛋疼啊。。。。。
啊,,好蛋疼啊。。。。。
啊,,好蛋疼啊。。。。。
是的,,,事實上,,我們可以發現四個頁面:
本學期成績: http://bksjw.chd.edu.cn/bxqcjcxAction.do
學年成績:http://bksjw.chd.edu.cn/gradeLnAllAction.do?type=ln&oper=qbinfo&lnxndm
方案成績:http://bksjw.chd.edu.cn/gradeLnAllAction.do?type=ln&oper=fainfo&fajhh=3792
打印成績:http://bksjw.chd.edu.cn/reportFiles/student/cj_zwcjd_all.jsp

拋開這些,試着點擊一下注銷,,我靠,我們從信息門戶跳轉到了URP教務系統。。
實際上,信息門戶的系統只是以框架的形式將URP教務系統的頁面嵌套進來,那就不奇怪了!

CR2

更神奇的是,,,,,啊,,在沒有修改密碼的情況下,, 所有人。。。。。是的,是所有人。。。。
*默認帳號密碼都是自己的學號!!!!! 自己的學號,己的學號,的學號~~~~~*

所以,,,我們下面。直接拿URP來開刀,,,,查詢成績以及計算績點!!

1. 模擬登陸URP教務系統

1.1 頁面分析

  • 打開IE瀏覽器 -> 按F12打開調試工具 -> 進入URP綜合教務系統 的 主頁

CR3

  • 輸入帳號密碼,登陸系統,頁面跳轉,觀察頁面的信息
    • IE下看不到表單數據,但是非IE內核卻又不能查看成績,怎么辦,,先到非IE內核瀏覽器下查看表單數據,再回來?其實你可以用其他抓包工具啊,比如 httpwatch 吖。
    • 另外一個問題需要明白,我們post的URL並非是主頁http://bksjw.chd.edu.cn/
      而是`http://bksjw.chd.edu.cn/loginAction.do```,這個我們在分析過程中是可以發現的

CR4

  • 我們可以看到headers,cookies,postdata等我們需要的數據

CR5

模擬登陸的關鍵就是這個 Form Data

1.2 嘗試登陸

#coding=utf-8
import urllib,urllib2,cookielib

# hosturl用於獲取cookies, posturl是登陸請求的URL
hosturl = 'http://bksjw.chd.edu.cn/'
posturl = 'http://bksjw.chd.edu.cn/loginAction.do' 

#獲取cookies
cj = cookielib.LWPCookieJar()  
cookie_support = urllib2.HTTPCookieProcessor(cj)  
opener = urllib2.build_opener(cookie_support, urllib2.HTTPHandler)  
urllib2.install_opener(opener)  
h = urllib2.urlopen(hosturl)  

# 偽裝成瀏覽器,反“反爬蟲”——雖然我們學校的URP好像沒有做反爬蟲
headers = {'User-Agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)',
    'Referer':'http://bksjw.chd.edu.cn/'} 
# 構造POST數據 用戶名和密碼,,自行修改啊,,別亂來啊。
postData = {
    'dllx':'dldl',
    'zjh':'xxxx',
    'mm':'xxxx' }
postData = urllib.urlencode(postData)
# 構造請求
request = urllib2.Request( posturl, postData, headers ) 
# 登陸
urllib2.urlopen(request)

2. 查詢成績

好的,我們成功模擬登陸URP系統,接下來。。繼續分析吧。。。
查看源代碼。。。發現,,這個URP系統是一個框架套一個框架。簡直爆炸。。
沒關系,我們仔細找,最終可以找到類似如下的地址,,是的,這正是我們需要的,其實就是我上面說的那四個。。。。。

CR6

我們接着寫

#coding=utf-8
import urllib,urllib2,cookielib

# hosturl用於獲取cookies, posturl是登陸請求的URL
hosturl = 'http://bksjw.chd.edu.cn/'
posturl = 'http://bksjw.chd.edu.cn/loginAction.do' 

#獲取cookies
cj = cookielib.LWPCookieJar()  
cookie_support = urllib2.HTTPCookieProcessor(cj)  
opener = urllib2.build_opener(cookie_support, urllib2.HTTPHandler)  
urllib2.install_opener(opener)  
h = urllib2.urlopen(hosturl)  

# 偽裝成瀏覽器,反“反爬蟲”——雖然我們學校的URP好像沒有做反爬蟲
headers = {'User-Agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)',
    'Referer':'http://bksjw.chd.edu.cn/'} 
# 構造POST數據
postData = {
    'dllx':'dldl',
    'zjh':'201224060201',
    'mm':'XXXXXXXX' }
postData = urllib.urlencode(postData)
# 構造請求
request = urllib2.Request( posturl, postData, headers ) 
# 登陸
urllib2.urlopen(request)

# 用一個方案成績做測試
testurl = 'http://bksjw.chd.edu.cn/gradeLnAllAction.do?type=ln&oper=fainfo&fajhh=3792'
save = urllib2.urlopen(testurl).read()
open( 'score.html', "w").write(save)

可以看到,程序運行之后,桌面上產生了一個 score.html 我們查詢到了自己的方案成績,nice!

CR7

一個應該注意的問題

在linux下html可能會出現亂碼,原因是,保存下來的html代碼並沒有指明編碼方式,好蛋疼的代碼啊
我們用如下方法來處理:

testurl = 'http://bksjw.chd.edu.cn/gradeLnAllAction.do?type=ln&oper=fainfo&fajhh=3792'
head = '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">' 
save = head + urllib2.urlopen(testurl).read().decode('GBK').encode('UTF-8')
open( 'score.html', "w").write(save)

3. 計算績點

還是看源代碼,,發現,,,,好垃圾的代碼,,好亂的代碼。。。。。。無語
最后可以發現,每門成績對應的html代碼如下:

CR8

大框架是一個<tr>xxxxxxx</tr>,,開始嘗試寫正則表達式
卻發現又存在一個問題,,,空格好多,,而且代碼好亂
怎么辦。。。強行寫了一個很難看的RE表達式:

reg = re.compile(r'<tr class="odd".*?>.*?<td.*?</td>.*?<td.*?</td>.*?<td.*?</td>.*?<td.*?</td>.*?<td align="center">\s*(\S+)\s*</td>.*?<td.*?</td>.*?<td align="center">.*?<p align="center">(.*?)&nbsp.*?</P>.*?</td>.*?<td.*?</td>.*?</tr>.*?',re.S)

使用正則表達式匹配出每門課的學分和分數,相乘之后相加,最后再除以總學分數,就OK了

\[GPA =\frac{\sum credit \times grade }{\sum credit } \]

然而,還有一個問題需要解決,,我的天,,,有些成績給的是等級「優秀、良好之類雲雲」,而不是分數。
好吧,我們首先把字符統一轉化為utf-8,然后再進行判斷,轉化為浮點型進行計算
那就有了如下函數:

def calc_gpa(self):

    reg = re.compile(r'<tr class="odd".*?>.*?<td.*?</td>.*?<td.*?</td>.*?<td.*?</td>.*?<td.*?</td>.*?<td align="center">\s*(\S+)\s*</td>.*?<td.*?</td>.*?<td align="center">.*?<p align="center">(.*?)&nbsp.*?</P>.*?</td>.*?<td.*?</td>.*?</tr>.*?',re.S)
    myItems = reg.findall(self.score_html)
    score = []
    credit = []
    sum = 0.0
    weight = 0.0

    for item in myItems:
        credit.append(item[0])
        score.append(item[1])
    
    for i in range(len(score)):
        try:
            we = float(credit[i])
            add = float(score[i])
            sum += add*we
            weight += we
        except:
            if score[i] == "優秀":    
                sum += 95.0*we
                weight += we
            elif score[i] == "良好":  
                sum += 85.0*we
                weight += we
            elif score[i] == "中等":  
                sum += 75.0*we
                weight += we
            elif score[i] == "及格":  
                sum += 60.0*we
                weight += we
            else:
                weight += we
    if weight == 0 :
        return
    print 'your GPA is ', sum/weight

4. 最終成果

CR9

是的,最后我實現了以四種模式查看成績,計算GPA兩個功能。

5. 一點擴展

如果是在windows下,我們還可以 使用 PyInstaller 把python程序 .py轉為 .exe 可執行程序
我只說一點,,,,切記不要放在中文目錄,,否則你會死的很難看。。。。

CR10

6. 源代碼

代碼有點撮不是很想貼,,你可以在我的 github 中找到,算了,還是貼出來吧

#!/usr/bin/python  
# -*- coding: utf-8 -*-
__author__ = 'BG'  

import urllib, urllib2  
import cookielib
import re

class URP:

    def __init__(self,username,password):
        self.usr = username
        self.psw = password
        self.hosturl = 'http://bksjw.chd.edu.cn/'
        self.posturl = 'http://bksjw.chd.edu.cn/loginAction.do' 

        cj = cookielib.LWPCookieJar()  
        cookie_support = urllib2.HTTPCookieProcessor(cj)  
        opener = urllib2.build_opener(cookie_support, urllib2.HTTPHandler)  
        urllib2.install_opener(opener)  
        h = urllib2.urlopen(self.hosturl)  

        self.headers = {'User-Agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729)',
            'Referer':'http://bksjw.chd.edu.cn/'} 
        self.postData = {'dllx':'dldl','zjh':self.usr,'mm':self.psw }
        self.postData = urllib.urlencode(self.postData)
        self.request = urllib2.Request( self.posturl, self.postData, self.headers )  

    def login(self):
        flag = False
        try:
            urllib2.urlopen(self.request)
            urllib2.urlopen('http://bksjw.chd.edu.cn/gradeLnAllAction.do?type=ln&oper=qbinfo&lnxndm').read()
        except urllib2.HTTPError, e:
            print '-----------------------------------------------------------'
            print 'Login Failed [%s], maybe your username or password is error!'  %e.code
            print '-----------------------------------------------------------'
        else:
            print '-----------------------------------------------------------'
            print 'Login Successful'
            print '-----------------------------------------------------------'
            flag = True
        return flag
            
    def BXQ_score(self):
        self.score_html = urllib2.urlopen('http://bksjw.chd.edu.cn/bxqcjcxAction.do').read()

    def NJ_score(self):
        self.score_html = urllib2.urlopen('http://bksjw.chd.edu.cn/gradeLnAllAction.do?type=ln&oper=qbinfo&lnxndm').read()

    def TAB_score(self):
        self.score_html = urllib2.urlopen('http://bksjw.chd.edu.cn/reportFiles/student/cj_zwcjd_all.jsp').read()

    def FA_score(self):
        self.score_html = urllib2.urlopen('http://bksjw.chd.edu.cn/gradeLnAllAction.do?type=ln&oper=fainfo&fajhh=3792').read()
    
    def calc_gpa(self):
        reg = re.compile(r'<tr class="odd".*?>.*?<td.*?</td>.*?<td.*?</td>.*?<td.*?</td>.*?<td.*?</td>.*?<td align="center">\s*(\S+)\s*</td>.*?<td.*?</td>.*?<td align="center">.*?<p align="center">(.*?)&nbsp.*?</P>.*?</td>.*?<td.*?</td>.*?</tr>.*?',re.S)
        myItems = reg.findall(self.score_html)

        score = []
        credit = []
        sum = 0.0
        weight = 0.0

        for item in myItems:
            credit.append(item[0])
            score.append(item[1])
        
        for i in range(len(score)):
            try:
                we = float(credit[i])
                add = float(score[i])
                sum += add*we
                weight += we
            except:
                if score[i] == "優秀":    
                    sum += 95.0*we
                    weight += we
                elif score[i] == "良好":  
                    sum += 85.0*we
                    weight += we
                elif score[i] == "中等":  
                    sum += 75.0*we
                    weight += we
                elif score[i] == "及格":  
                    sum += 60.0*we
                    weight += we
                else:
                    weight += we
        if weight == 0 :
            return
        print 'your GPA is ', sum/weight


    def save_html(self):

        fout=open("score.html","w")  
        head = '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">' 
        self.score_html = head + self.score_html.decode('GBK').encode('UTF-8')
        fout.write(self.score_html)  
        print '-----------------------------------------------------------'
        print 'The result was saved in score.html\nGod bless you!!!!!!'


def query_score():
    print 'Please input your username:'
    usr = raw_input()
    print 'Please input your password:'
    psw = raw_input()

    urp = URP(usr,psw)
    if urp.login() == True:
        print 'Please choose an mode:'
        print '1 - your score of each subject in this term'
        print '2 - all of your score in each term'
        print '3 - all of your score in a table'
        print '4 - all of your score by plan'

        choose = raw_input()
        if choose == '1':
            urp.BXQ_score()
        elif choose == '2':
            urp.NJ_score()
        elif choose == '3':
            urp.TAB_score()
        else:
            urp.FA_score()

        urp.save_html()
        urp.calc_gpa()
    else:
        return

if __name__ == '__main__':
    query_score()
    raw_input ('Press Enter to exit!')


7. TODO

我又更新了一個 版本 ,可以獲取自己的證件照。。其實就是入學時候的身份證照片。丑到爆。。
本來想把全年級同學的照片都爬下來,后來想想不是很道德。還是算了吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM