1. 程式人生 > >[python爬蟲]爬取學校教務處以及登入過程驗證碼的處理

[python爬蟲]爬取學校教務處以及登入過程驗證碼的處理

其實是半年前做的一段小程式碼,爬取自己的學校教務處網站大概是每個學習爬蟲的同學的入門必備吧(心疼一秒教務處)。其實想起來本科的時候有大神做了南理工GPA的網頁,其實也就是個爬蟲然後做了資料處理(只是我的猜測啦,不是請不要拍我。。),當時的教務處系統還比較簡單,也沒有驗證碼的問題,post一個表單就可以模擬登陸。但是!南理工教務處他改版了,還做的很不錯(大概招了個不錯的前端),加了驗證碼大概是這次爬蟲比較大的問題了。

1.登陸過程分析

利用chrome的開發者工具,可以清楚地看出南理工研究生院登陸的過程是怎樣的。首先,登陸頁面長這樣:

開啟chrome的更多工具中的開發者工具,選擇network,把preserve log勾上(這樣才能把中間過程儲存下來),好了,輸入你的使用者名稱密碼以及驗證碼,點選登陸。看一下出現了一個POST方法,這個就是我們提交表單的資訊了,點進去拖到最下方可以看到from data,是不是看到了你提交的使用者名稱密碼之類的資訊,接下來用程式碼模擬登陸的話把這個data裡的資訊提交就可以了。

同時,登陸的時候也需要提供請求頭,這個在剛剛那個post方法下面也是可以看到的。具體每個域有什麼作用這個應該的HTTP協議中用的了。

Host頭域指定請求資源的Intenet主機和埠號,必須表示請求url的原始伺服器或閘道器的位置。HTTP/1.1請求必須包含主機頭域,否則系統會以400狀態碼返回。
Accept:告訴WEB伺服器自己接受什麼介質型別,/ 表示任何型別,type/* 表示該型別下的所有子型別,type/sub-type。
Accept-Charset: 瀏覽器申明自己接收的字符集。
Authorization:當客戶端接收到來自WEB伺服器的 WWW-Authenticate 響應時,用該頭部來回應自己的身份驗證資訊給WEB伺服器。
User-Agent頭域的內容包含發出請求的使用者資訊。
Referer 頭域允許客戶端指定請求uri的源資源地址,這可以允許伺服器生成回退連結串列,可用來登陸、優化cache等。如果請求的uri沒有自己的uri地址,Referer不能被髮送。

2.模擬登陸爬取自己的資訊

用cookiejar來儲存cookie資訊,然後使用urllib2的HTTPCookieProcessor來建立cookie處理器,通過此處理器來構建opener。這也是大部分模擬登陸採用的方法。

這裡比較麻煩的是怎樣獲取驗證碼的圖片。分析網頁可以看到驗證碼圖片的url是http://gsmis.njust.edu.cn/Public/ValidateCode.aspx?image=,下面直接給出獲取驗證碼圖片的一段程式碼:

def getUrlRespHtml(url,head) :
    resp = getUrlResponse(url,head)
    respHtml = resp.read()
    return respHtml 
    
def getCheckCode(url,postdic,headerdic):
    print "+"*20+"getCheckCode"+"+"*20
#    response = urllib2.urlopen(url)
    respHtml = getUrlRespHtml(url,headerdic)
    img = Image.open(cStringIO.StringIO(respHtml))
    img.show()
    checkCode = raw_input("code:") 
    print 'aaa'
    postdic["ValidateCode"] = checkCode
    return postdic

可以看到用了cStringIO直接把圖片儲存為記憶體裡的一個臨時檔案,然後用Image開啟,這樣程式碼執行的時候驗證碼會直接彈出來,填完了關掉就可以了,當然也可以儲存到本地,然後到目標地址開啟讀取,不過我覺得這樣比較快。另外要注意一定要先建立cookie,再獲取驗證碼進行提交,不然相當於你填完了驗證碼又重新整理了一下,驗證碼就變掉了,這樣會提示你驗證碼錯誤的。

把自己的使用者名稱密碼驗證碼以及一些其他的資訊作為postdata就可以模擬登陸了,問題又來了,新的研究生院網站是aspx的,開啟每個子網頁的url都是http://gsmis.njust.edu.cn/Gstudent/Default.aspx,怎麼才能定位到開題報告的地方呢?這裡肯定有比較好的方法的,但是畢竟個人管理系統內容比較簡單,於是我想了一個很蠢的辦法。。用開發者工具可以看到顯示開題報告狀態的表格的的url為http://gsmis.njust.edu.cn/Gstudent/Topic/Topic_Manage.aspx?EID=ZfJPCMhdYM0Q2rZHdgSidgWH2ovrUbhP6ygVVycyPf0=,把這個網址輸入到位址列發現的確可以開啟。如果我只要這個資訊的話,那麼用獲得的cookies直接開啟這個url就可以了。爬取的資訊如下圖。當然可以進一步用正則表示式或者beautifulsoup對爬取的資訊進行分析。


下面給出完整程式碼:

# -*- coding: utf-8 -*-
"""
Created on Mon Dec 28 21:49:49 2015

@author: DJ
"""

import urllib
import urllib2
import cookielib
	
from bs4 import BeautifulSoup
from PIL import Image
import cStringIO
#登入地址
LoginUrl = "http://gsmis.njust.edu.cn/UserLogin.aspx?exit=1"
mainpage="http://http://gsmis.njust.edu.cn/Gstudent/Default.aspx"
checkCodeUrl = ''
kaitibaogao='http://gsmis.njust.edu.cn/Gstudent/Topic/Topic_Manage.aspx?EID=ZfJPCMhdYM0Q2rZHdgSidgWH2ovrUbhP6ygVVycyPf0='

#登入主函式
def login():
    headerdic={
        'User-Agent'    : 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36',
        'Host' : 'gsmis.njust.edu.cn',
        'Referer' : 'http://gsmis.njust.edu.cn/',
        'Accept'        : '*/*',
        'Connection' : 'Keep-Alive',
         }

    postdic={
         'UserName' : yourusername ,
         'PassWord' : yourpassword,
         'drpLoginType' : '1',
         '__ASYNCPOST' : 'true',
         'ScriptManager1' : 'UpdatePanel2|btLogin',
         '__EVENTTARGET' : 'btLogin',
         '__VIEWSTATE' : '/wEPDwULLTE5OTkyNTM4MzEPZBYCAgMPZBYGAg0PZBYCZg9kFgICAQ8PFgIeCEltYWdlVXJsBSp+L1B1YmxpYy9WYWxpZGF0ZUNvZGUuYXNweD9pbWFnZT02ODA3OTUyOTFkZAIRD2QWAmYPZBYCAgEPEGRkFgFmZAIVD2QWAmYPZBYCAgEPDxYCHgtOYXZpZ2F0ZVVybAUtfi9QdWJsaWMvRW1haWxHZXRQYXNzd2QuYXNweD9FSUQ9VHVyOHZadXVYa3M9ZGQYAQUeX19Db250cm9sc1JlcXVpcmVQb3N0QmFja0tleV9fFgEFDVZhbGlkYXRlSW1hZ2W5HJlvYqz666q9lGAspojpOWb4sA==',
         '__EVENTVALIDATION' : '/wEdAAoKNGMKLh/WwBcPaLKBGC94R1LBKX1P1xh290RQyTesRQa+ROBMEf7egV772v+RsRJUvPovksJgUuQnp+WD/+4LQKymBEaZgVw9rfDiAaM1opWKhJheoUmouOqQCzlwTSNWlQTw3DcvmMLY3PAqFoA+uFSTy5ozCEG4XBxL/Ykep0cgC/Irwlr9d8VObb8MnYO0GRqRfbdgDIW2dtIsr6rbUIwej/LsqVAg3gLMpVY6UeARlz0=',
         '__EVENTARGUMENT' : '',
         '__LASTFOCUS' : ''
         }

    #cookie 自動處理器
    global checkCodeUrl
    cookiejar = cookielib.LWPCookieJar()#LWPCookieJar提供可讀寫操作的cookie檔案,儲存cookie物件
    cookieSupport= urllib2.HTTPCookieProcessor(cookiejar)
    opener = urllib2.build_opener(cookieSupport, urllib2.HTTPHandler)
    urllib2.install_opener(opener)
    #開啟登陸頁面
    img_url="http://gsmis.njust.edu.cn/Public/ValidateCode.aspx?image="
    getCheckCode(img_url,postdic,headerdic)
#    print postdic
    postData=urllib.urlencode(postdic)
    sendPostData(LoginUrl, postData, headerdic,opener)
#    print opener.open(kaitibaogao).read()
    ktbgtxt=opener.open(kaitibaogao).read()
    print ktbgtxt
    soup=BeautifulSoup(ktbgtxt)
    print soup.td    
    


def sendPostData(url, data, header,opener):
    print "+"*20+"sendPostData"+"+"*20
#    data = urllib.urlencode(data)      
    request = urllib2.Request(url, data, header)
    response = opener.open(request)
    url = response.geturl()
    text = response.read()
    print url
    print text

 

def getUrlResponse(url,head) :
    url = str(url)
    req = urllib2.Request(url)
    for eachhead in head.keys():
        req.add_header(eachhead,head[eachhead])
        
    resp = urllib2.urlopen(req)  
    return resp

def getUrlRespHtml(url,head) :
    resp = getUrlResponse(url,head)
    respHtml = resp.read()
    return respHtml 
    
def getCheckCode(url,postdic,headerdic):
    print "+"*20+"getCheckCode"+"+"*20
#    response = urllib2.urlopen(url)
    respHtml = getUrlRespHtml(url,headerdic)
    img = Image.open(cStringIO.StringIO(respHtml))
    img.show()
    checkCode = raw_input("code:") 
    print 'aaa'
    postdic["ValidateCode"] = checkCode
    return postdic

 
if __name__ == "__main__":   
    login()