Python爬蟲實戰 抓取圖書館借閱信息 原創作品,引用請表明出處:Python爬蟲實戰 抓取圖書館借閱信息 前段時間在圖書館借了很多書,借得多了就容易忘記每本書的應還日期,老是擔心自己會違約,影響日後借書,而自己又懶得總是登錄到學校圖書館借閱系統查看,於是就打算寫一個爬蟲來抓取自己的借閱信息,把每 ...
Python爬蟲實戰---抓取圖書館借閱信息
原創作品,引用請表明出處:Python爬蟲實戰---抓取圖書館借閱信息
前段時間在圖書館借了很多書,借得多了就容易忘記每本書的應還日期,老是擔心自己會違約,影響日後借書,而自己又懶得總是登錄到學校圖書館借閱系統查看,於是就打算寫一個爬蟲來抓取自己的借閱信息,把每本書的應還日期給爬下來,並寫入txt文件,這樣每次忘了就可以打開該txt文件查看,每次借閱信息改變了,只要再重新運行一遍該程式,原txt文件就會被新文件覆蓋,裡面的內容得到更新。
用到的技術:
Python版本是 2.7 ,同時用到了urllib2、cookielib、re三個模塊。urllib2用於創建請求(request),並抓取網頁信息,返回一個類似於文件類型的response對象;cookielib用於儲存cookie對象,以實現模擬登錄功能;re模塊提供對正則表達式的支持,用於對抓取到的頁面信息進行匹配,以得到自己想要的信息。
抓取一個頁面:
使用urllib2簡單抓取一個網頁的過程非常簡單:
1 import urllib2 2 response = urllib2.urlopen("http://www.baidu.com") 3 html = response.read()
urllib2中的urlopen()方法,看其字面意思就知道是打開一個URL(uniform resource locator)地址,上面例子傳入的時百度首頁的地址,遵循HTTP協議,除了http協議外,urlopen()方法還可以打開遵循ftp、file協議的地址,如:
1 response = urllib2.urlopen("ftp://example.com")
除URL參數外,urlopen()方法還接受data和timeout參數:
1 response = urllib2.urlopen(url ,data ,timeout)
其中data是打開一個網頁時需要傳入的數據,比如打開一個登錄界面時往往需要傳入用戶名和密碼等信息,在下文登錄圖書館系統時將會看到其用法;timeout是設置超時時間,即超過一定時間頁面無響應即報錯;在urlopen()方法中,data和timeout不是必須的,即可填可不填,註意:當頁面需要有數據傳入時,data是必需的。
可以看到,在打開一個網頁時,有時往往需要傳入多個參數,再加上HTTP協議是基於請求(request)和應答(response)的,即客戶端發出請求(request),伺服器端返回應答(response),所以在使用urlopen()方法時,往往是構造一個request對象作為參數傳入,該request對象包括url、data、timeout、headers等信息:
1 import urllib2 2 request = urllib2.Request("http://www.baidu.com") 3 response = urllib2.urlopen(request) 4 html = response.read()
這段代碼得到的結果和上面得到的結果一樣,但是在邏輯上顯得更明確、清晰。
Cookie的使用:
在訪問某些網站時,該網站需要在客戶端本地儲存一些數據、信息(經過加密),併在接下來的請求(request)中返回給伺服器,否則伺服器將拒絕該請求,這些數據即存儲在本地的cookie中。例如,訪問學校圖書館系統時,需進行登錄,等登錄完成之後,伺服器端將會在本地儲存一些經過加密的數據在cookie中,當客戶端發送查詢借閱信息的請求(request)時,會連帶cookie裡面的數據一起發送給伺服器,伺服器確定cookie信息後允許訪問,否則拒絕該請求。
Cookielib模塊提供了CookieJar類用於捕捉和儲存HTTP 的cookie數據,所以要創建一個cookie只要創建一個CookieJar實例即可:
1 import cookielib 2 cookie = coolielib.CookieJar()
創建cookie了就萬事大吉了嗎?沒那麼簡單,我們要完成的操作是發送登錄請求、記錄cookie、再發送讀取借閱信息的請求並向伺服器反饋cookie信息,要完成這一系列的操作,原來的urlopen()方法已不能勝任,幸運的是,urllib2模塊還提供了一個OpenerDirector類,可以接受一個cookie處理器為參數,實現上述功能,而這個cookie處理器則可以通過HTTPCookieProcessor類接受一個cookie對象實例化後得到。即先通過HTTPCookieProcessor實例化得到一個cookie處理器handler,再將此處理器headler作為參數傳入OpenDirector實例化得到一個能捕捉cookie數據的opener,代碼如下:
1 import urllib2 2 import cookielib 3 4 cookie = cookielib.CookieJar() 5 handler = urllib2.HTTPCookieProcessor(cookie) 6 opener = urllib2.build_opener(handler) 7 response = opener.open("http://www.baidu.com")
登錄圖書館系統:
至此,我們就可以進行圖書館借閱信息的抓取了。來看看hit圖書館登錄界面:
首先,在Firefox瀏覽器下,藉助httpfox插件進行網路監聽,看看登錄此頁面需要向伺服器發送哪些數據:
輸入登錄賬號和密碼,打開httpfox插件,點擊start開始監聽,然後點擊登陸按鈕進行登陸:
上圖便是登陸之後的頁面,以及整個登陸過程捕捉到的信息。選擇第一條捕捉到的信息,點擊下方數據頭(Headers)選項卡,可以看見登錄此頁面需要發送的一些數據。有一些網站,對於訪問它們的請求需要檢查數據頭(Headers),只有數據頭信息符合要求才允許訪問。在登錄圖書館系統時,可以先嘗試不發數據頭,如果能順利訪問則說明沒有Headers檢查這一環節。數據發送的方法為GET,即只需要將要發送的數據信息加在登陸請求的後面。在Headers選項卡的Request-Line屬性中,問號前面的即為登陸請求"GET /lib/opacAction.do",加上IP地址之後真實的請求URL為"http://202.118.250.131/lib/opacAction.do",問號後面的即為登陸需要的數據,包括賬號、密碼等信息。
接下來點開QueryString選項卡,查看由GET方法傳送的數據:
需要傳送的數據包括5項,以字典類型將其儲存,經過urlencode()方法編碼之後直接加在登陸URL之後即可,所以最後向伺服器發送的請求(request)為:
1 import urllib 2 3 loginURL = 'http://202.118.250.131/lib/opacAction.do' 4 queryString = urllib.urlencode({ 5 'method':'DoAjax', 6 'dispatch':'login', 7 'registerName':'', 8 'rcardNo':'16S137028 0', 9 'pwd':'******' 10 }) 11 requestURL = self.loginURL + '?' + self.queryString
得到請求URL之後就可以模擬登陸圖書館系統了,在模擬登陸的過程中需要用到前面講到的cookie,否則無法進行後續的訪問。在編代碼過程中,定義一個library類,使訪問過程變成一個面向對象的過程,可以根據需要實例化多個library對象,分別對多個實例進行操作。首先分析,該library類應該有一個初始化方法(__init__)以及一個獲取頁面的方法(getPage),在打開網頁是,應使用上文提到opener實例,自動捕獲並儲存cookie:
1 import urllib 2 import urllib2 3 import cookielib 4 import re 5 6 class library: 7 def __init__(self): 8 self.loginURL='http://202.118.250.131/lib/opacAction.do' 9 self.queryString = urllib.urlencode({ 10 'method':'DoAjax', 11 'dispatch':'login', 12 'registerName':'', 13 'rcardNo':'16S137028 0', 14 'pwd':'******' 15 }) 16 self.requestURL = self.loginURL + '?' + self.queryString 17 self.cookies=cookielib.CookieJar() 18 self.handler=urllib2.HTTPCookieProcessor(self.cookies) 19 self.opener=urllib2.build_opener(self.handler) 20 def getPage(self): 21 request1 = urllib2.Request(self.requestURL) 22 request2 = urllib2.Request(' http://202.118.250.131/lib/opacAction.do?method=init&seq=301 ') 23 result = self.opener.open(request1) 24 result = self.opener.open(request2) 25 return result.read() 26 27 lib = library() 28 print lib.getPage()
上述代碼中,先是進行登錄 result = self.opener.open(request1) ,登錄沒有異常,說明登錄過程不用檢查數據頭;然後再用此 self.opener 打開借閱查詢頁面
http://202.118.250.131/lib/opacAction.do?method=init&seq=301 ,所以這段代碼將列印借閱查詢界面的HTML代碼,下圖是部分列印結果:
獲取借閱信息:
抓取了頁面信息之後,接下來就是根據自己的需要匹配、儲存信息了。在匹配頁面信息時,這裡用的是正則表達式的方式進行匹配,正則表達式的支持由Python的Re模塊提供支持,關於如何使用正則表達式,可以參考這裡:Python正則表達式指南
使用Re模塊進行匹配時,往往先將正則表達式字元串編譯(compile)成一個Pattern實例,再利用Re模塊中的re.findall(pattern , string),將字元串string中和正則表達式匹配的數據以列表的形式返回。如果在pattern中有超過一個組(group),則返回的結果將是一個元組列表,如此正則表達式: <table.*?id="tb.*?width="50%"><font size=2>(.*?)</font>.*?<tr>.*?<tr>.*?<font size=2>(.*?)</font>.*?<font size=2>(.*?)</font>.*?</TABLE> ,式中,每一個 (.*?) 代表一個組,即此式中有3個組,則匹配時,返回一個元組列表,其中每一個元組又有3個數據。
在library類中,定義一個獲取信息的方法(getInformation),以通過正則表達式匹配的方式獲取所需數據:
1 def getInformation(self): 2 page = self.getPage() 3 pattern = re.compile('<table.*?id="tb.*?width="50%"><font size=2>(.*?)</font>.*?<tr>.*?<tr>.*?'+ 4 '<font size=2>(.*?)</font>.*?<font size=2>(.*?)</font>.*?</TABLE>',re.S) 5 items = re.findall(pattern,page)
獲取所需數據之後,接下來就是將數據寫入文本文件(txt)儲存,以讀寫模式(W+)打開一個文件(library.txt),然後通過write()方法將數據一條一條的寫入文件。不過,在信息寫入之前,需要對抓取到的信息做一些小處理,剛纔說過了,findall()方法返回的是一個元組列表,即[[a,b,c],[d,e,f],[g,h,i]]的形式,write()方法是不能對元組進行操作的,所以需要手動將元組翻譯成一條條字元串,再保存到一個列表裡,通過遍歷將每條字元串寫入文件:
1 def getInformation(self): 2 page = self.getPage() 3 pattern = re.compile('<table.*?id="tb.*?width="50%"><font size=2>(.*?)</font>.*?<tr>.*?<tr>.*?'+ 4 '<font size=2>(.*?)</font>.*?<font size=2>(.*?)</font>.*?</TABLE>',re.S) 5 items = re.findall(pattern,page) 6 7 contents = [] 8 for item in items: 9 content = item[0]+' from '+item[1]+' to '+item[2]+'\n' 10 contents.append(content) 11 self.writeData(contents) 12 def writeData(self,contents): 13 file = open('libraryBooks.txt','w+') 14 for content in contents: 15 file.write(content) 16 file.close()
至此,整個爬蟲就算完成了,下麵貼上完整代碼:
大功告成:
1 __author__='Victor' 2 #_*_ coding:'utf-8' _*_ 3 import urllib 4 import urllib2 5 import cookielib 6 import re 7 8 class library: 9 def __init__(self): 10 self.loginURL='http://202.118.250.131/lib/opacAction.do' 11 self.queryString = urllib.urlencode({ 12 'method':'DoAjax', 13 'dispatch':'login', 14 'registerName':'', 15 'rcardNo':'16S137028 0', 16 'pwd':'******' 17 }) 18 self.requestURL = self.loginURL + '?' + self.queryString 19 self.cookies=cookielib.CookieJar() 20 self.handler=urllib2.HTTPCookieProcessor(self.cookies) 21 self.opener=urllib2.build_opener(self.handler) 22 def getPage(self): 23 request1 = urllib2.Request(self.requestURL) 24 request2 = urllib2.Request('http://202.118.250.131/lib/opacAction.do?method=init&seq=301') 25 result = self.opener.open(request1) 26 result = self.opener.open(request2) 27 return result.read() 28 def getInformation(self): 29 page = self.getPage() 30 pattern = re.compile('<table.*?id="tb.*?width="50%"><font size=2>(.*?)</font>.*?<tr>.*?<tr>.*?'+ 31 '<font size=2>(.*?)</font>.*?<font size=2>(.*?)</font>.*?</TABLE>',re.S) 32 items = re.findall(pattern,page) 33 34 contents = [] 35 for item in items: 36 content = item[0]+' from '+item[1]+' to '+item[2]+'\n' 37 contents.append(content) 38 self.writeData(contents) 39 def writeData(self,contents): 40 file = open('libraryBooks.txt','w+') 41 for content in contents: 42 file.write(content) 43 file.close() 44 45 lib = library() 46 lib.getInformation()
下麵就是抓到的借閱信息,不得不說效果不怎麼樣,不過還是湊合著看把:
原創作品,引用請表明出處:Python爬蟲實戰---抓取圖書館借閱信息