大家好,本次為大家?guī)淼捻椖渴怯嬎愦髮W本學期績點。首先說明的是,博主來自山東大學,有屬于個人的學生成績管理系統(tǒng),需要學號密碼才可以登錄,不過可能廣大讀者沒有這個學號密碼,不能實際進行操作,所以最主要的還是獲取它的原理。最主要的是了解cookie的相關操作。
本篇目標
模擬登錄學生成績管理系統(tǒng)
抓取本學期成績界面
恩,博主來自山東大學~
先貼一個 URL,讓大家知道我們學校學生信息系統(tǒng)的網(wǎng)站構架,主頁是 http://jwxt.sdu.edu.cn:7890/zhxt_bks/zhxt_bks.html,山東大學學生個人信息系統(tǒng),進去之后,Oh 不,他竟然用了frame,一個多么古老的而又任性的寫法,真是驚出一身冷汗~
算了,就算他是frame又能拿我怎么樣?我們點到登錄界面,審查一下元素,先看看登錄界面的URL 是怎樣的?
http://wiki.jikexueyuan.com/project/python-crawler-guide/images/14.png" alt="" />
恩,看到了右側的 frame 名稱,src=”xk_login.html”,可以分析出完整的登錄界面的網(wǎng)址為 http://jwxt.sdu.edu.cn:7890/zhxt_bks/xk_login.html,點進去看看,真是棒棒噠,他喵的竟然是清華大學選課系統(tǒng),醉了,你說你抄襲就抄襲吧,改改名字也不錯啊~
算了,就不和他計較了?,F(xiàn)在,我們登錄一下,用瀏覽器監(jiān)聽網(wǎng)絡。
我用的是獵豹瀏覽器,審查元素時會有一個網(wǎng)絡的選項,如果大家用的 Chrome,也有相對應的功能,F(xiàn)irefox 需要裝插件 HttpFox,同樣可以實現(xiàn)。
這個網(wǎng)絡監(jiān)聽功能可以監(jiān)聽表單的傳送以及請求頭,響應頭等等的信息。截個圖看一下,恩,我偷偷把密碼隱藏了,你看不到~
大家看到的是登錄之后出現(xiàn)的信息以及 NetWork 監(jiān)聽,顯示了 hearders 的詳細信息。
http://wiki.jikexueyuan.com/project/python-crawler-guide/images/15.png" alt="" />
最主要的內(nèi)容,我們可以發(fā)現(xiàn)有一個表單提交的過程,提交方式為 POST,兩個參數(shù)分別為 stuid 和 pwd。
請求的URL為 http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bks_login2.login,沒錯,找到表單數(shù)據(jù)和目標地址就是這么簡單。
在這里注意,剛才的 http://jwxt.sdu.edu.cn:7890/zhxt_bks/xk_login.html 只是登錄界面的地址,剛剛得到的這個地址才是登錄索要提交到的真正的URL。希望大家這里不要混淆。
不知道山大這個系統(tǒng)有沒有做headers的檢查,我們先不管這么多,先嘗試一下模擬登錄并保存Cookie。
好,通過以上信息,我們已經(jīng)找到了登錄的目標地址為 http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bks_login2.login
有一個表單提交到這個URL,表單的兩個內(nèi)容分別為 stuid 和 pwd,學號和密碼,沒有其他的隱藏信息,提交方式為 POST。
好,現(xiàn)在我們首先構造以下代碼來完成登錄。看看會不會獲取到登錄之后的提示頁面。
__author__ = 'CQC'
\# -*- coding:utf-8 -*-
import urllib
import urllib2
import cookielib
import re
\#山東大學績點運算
class SDU:
def __init__(self):
self.loginUrl = 'http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bks_login2.login'
self.cookies = cookielib.CookieJar()
self.postdata = urllib.urlencode({
'stuid':'201200131012',
'pwd':'xxxxxx'
})
self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookies))
def getPage(self):
request = urllib2.Request(
url = self.loginUrl,
data = self.postdata)
result = self.opener.open(request)
#打印登錄內(nèi)容
print result.read().decode('gbk')
sdu = SDU()
sdu.getPage()
測試一下,竟然成功了,山大這網(wǎng)竟然沒有做 headers 檢查,很順利就登錄進去了。
說明一下,在這里我們利用了前面所說的 cookie,用到了 CookieJar 這個對象來保存cookies,另外通過構建 opener,利用 open 方法實現(xiàn)了登錄。如果大家覺得這里有疑惑,請看 Python爬蟲入門六之Cookie的使用,這篇文章說得比較詳細。
好,我們看一下運行結果
http://wiki.jikexueyuan.com/project/python-crawler-guide/images/16.png" alt="" />
酸爽啊,接下來我們只要再獲取到本學期成績界面然后把成績抓取出來就好了。
讓我們先在瀏覽器中找到本學期成績界面,點擊左邊的本學期成績。
http://wiki.jikexueyuan.com/project/python-crawler-guide/images/17.png" alt="" />
重新審查元素,你會發(fā)現(xiàn)這個 frame 的 src 還是沒有變,仍然是 xk_login.html,引起這個頁面變化的原因是在左邊的本學期成績這個超鏈接設置了一個目標 frame,所以,那個頁面就顯示在右側了。
所以,讓我們再審查一下本學期成績這個超鏈接的內(nèi)容是什么~
http://wiki.jikexueyuan.com/project/python-crawler-guide/images/18.png" alt="" />
恩,找到它了,本學期成績。
那么,完整的URL就是 http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bkscjcx.curscopre,好,URL已經(jīng)找到了,我們繼續(xù)完善一下代碼,獲取這個頁面。
__author__ = 'CQC'
\# -*- coding:utf-8 -*-
import urllib
import urllib2
import cookielib
import re
\#山東大學績點運算
class SDU:
def __init__(self):
\#登錄URL
self.loginUrl = 'http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bks_login2.login'
\#本學期成績URL
self.gradeUrl = 'http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bkscjcx.curscopre'
self.cookies = cookielib.CookieJar()
self.postdata = urllib.urlencode({
'stuid':'201200131012',
'pwd':'xxxxxx'
})
\#構建opener
self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookies))
\#獲取本學期成績頁面
def getPage(self):
request = urllib2.Request(
url = self.loginUrl,
data = self.postdata)
result = self.opener.open(request)
result = self.opener.open(self.gradeUrl)
\#打印登錄內(nèi)容
print result.read().decode('gbk')
sdu = SDU()
sdu.getPage()
上面的代碼,我們最主要的是增加了
result = self.opener.open(self.gradeUrl)
這句代碼,用原來的 opener 訪問一個本學期成績的 URL 即可。運行結果如下
http://wiki.jikexueyuan.com/project/python-crawler-guide/images/19.png" alt="" />
恩,本學期成績的頁面已經(jīng)被我們抓取下來了,接下來用正則表達式提取一下,然后計算學分即可
接下來我們就把頁面內(nèi)容提取一下,最主要的便是學分以及分數(shù)了。
平均績點 = ∑(每科學分*每科分數(shù))/總學分
所以我們把每科的學分以及分數(shù)抓取下來就好了,對于有些課打了良好或者優(yōu)秀等級的,我們不進行抓取。
我們可以發(fā)現(xiàn)每一科都是TR標簽,然后是一系列的td標簽
<TR>
<td bgcolor="#EAE2F3"><p align="center"><INPUT TYPE="checkbox" NAME="p_pm" VALUE="013320131012015011294 面向對象技術"></p></td>
<td bgcolor="#EAE2F3"><p align="center">0133201310</p></td>
<td bgcolor="#EAE2F3"><p align="center">面向對象技術</p></td>
<td bgcolor="#EAE2F3"><p align="center">1</p></td>
<td bgcolor="#EAE2F3"><p align="center">2.5</p></td>
<td bgcolor="#EAE2F3"><p align="center">20150112</p></td>
<td bgcolor="#EAE2F3"><p align="center">94</p></td>
<td bgcolor="#EAE2F3"><p align="center">必修</p></td>
</TR>
我們用下面的正則表達式進行提取即可,部分代碼如下
page = self.getPage()
myItems = re.findall('<TR>.*?<p.*?<p.*?<p.*?<p.*?<p.*?>(.*?)</p>.*?<p.*?<p.*?>(.*?)</p>.*?</TR>',page,re.S)
for item in myItems:
self.credit.append(item[0].encode('gbk'))
self.grades.append(item[1].encode('gbk'))
主要利用了 findall 方法,這個方法在此就不多介紹了,前面我們已經(jīng)用過多次了。
得到的學分和分數(shù)我們都用列表 list 進行存儲,所以用了 append 方法,每獲取到一個信息就把它加進去。
恩,像上面那樣把學分績點都保存到列表 list 中了,所以我們最后用一個公式來計算學分績點就好了,最后整理后的代碼如下:
\# -*- coding: utf-8 -*-
import urllib
import urllib2
import cookielib
import re
import string
\#績點運算
class SDU:
\#類的初始化
def __init__(self):
\#登錄URL
self.loginUrl = 'http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bks_login2.login'
\#成績URL
self.gradeUrl = 'http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bkscjcx.curscopre'
\#CookieJar對象
self.cookies = cookielib.CookieJar()
\#表單數(shù)據(jù)
self.postdata = urllib.urlencode({
'stuid':'201200131012',
'pwd':'xxxxx'
})
\#構建opener
self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookies))
\#學分list
self.credit = []
\#成績list
self.grades = []
def getPage(self):
req = urllib2.Request(
url = self.loginUrl,
data = self.postdata)
result = self.opener.open(req)
result = self.opener.open(self.gradeUrl)
\#返回本學期成績頁面
return result.read().decode('gbk')
def getGrades(self):
\#獲得本學期成績頁面
page = self.getPage()
\#正則匹配
myItems = re.findall('<TR>.*?<p.*?<p.*?<p.*?<p.*?<p.*?>(.*?)</p>.*?<p.*?<p.*?>(.*?)</p>.*?</TR>',page,re.S)
for item in myItems:
self.credit.append(item[0].encode('gbk'))
self.grades.append(item[1].encode('gbk'))
self.getGrade()
def getGrade(self):
\#計算總績點
sum = 0.0
weight = 0.0
for i in range(len(self.credit)):
if(self.grades[i].isdigit()):
sum += string.atof(self.credit[i])*string.atof(self.grades[i])
weight += string.atof(self.credit[i])
print u"本學期績點為:",sum/weight
sdu = SDU()
sdu.getGrades()
好,最后就會打印輸出本學期績點是多少,小伙伴們最主要的了解上面的編程思路就好。
最主要的內(nèi)容就是 Cookie 的使用,模擬登錄的功能。
本文思路參考來源:汪海的爬蟲