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