天天看點

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      

 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 =      

這段代碼得到的結果和上面得到的結果一樣,但是在邏輯上顯得更明确、清晰。

Cookie的使用:  

  在通路某些網站時,該網站需要在用戶端本地儲存一些資料、資訊(經過加密),并在接下來的請求(request)中傳回給伺服器,否則伺服器将拒絕該請求,這些資料即存儲在本地的cookie中。例如,通路學校圖書館系統時,需進行登入,等登入完成之後,伺服器端将會在本地儲存一些經過加密的資料在cookie中,當用戶端發送查詢借閱資訊的請求(request)時,會連帶cookie裡面的資料一起發送給伺服器,伺服器确定cookie資訊後允許通路,否則拒絕該請求。

  Cookielib子產品提供了CookieJar類用于捕捉和儲存HTTP 的cookie資料,是以要建立一個cookie隻要建立一個CookieJar執行個體即可:

1 import cookielib
2      

 建立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​​圖書館登入界面​​:

                                       

Python爬蟲實戰---抓取圖書館借閱資訊

  首先,在Firefox浏覽器下,借助httpfox插件進行網絡監聽,看看登入此頁面需要向伺服器發送哪些資料:

Python爬蟲實戰---抓取圖書館借閱資訊

輸入登入賬号和密碼,打開httpfox插件,點選start開始監聽,然後點選登陸按鈕進行登陸:

Python爬蟲實戰---抓取圖書館借閱資訊

上圖便是登陸之後的頁面,以及整個登陸過程捕捉到的資訊。選擇第一條捕捉到的資訊,點選下方資料頭(Headers)頁籤,可以看見登入此頁面需要發送的一些資料。有一些網站,對于通路它們的請求需要檢查資料頭(Headers),隻有資料頭資訊符合要求才允許通路。在登入圖書館系統時,可以先嘗試不發資料頭,如果能順利通路則說明沒有Headers檢查這一環節。資料發送的方法為GET,即隻需要将要發送的資料資訊加在登陸請求的後面。在Headers頁籤的Request-Line屬性中,問号前面的即為登陸請求"GET /lib/opacAction.do",加上IP位址之後真實的請求URL為"http://202.118.250.131/lib/opacAction.do",問号後面的即為登陸需要的資料,包括賬号、密碼等資訊。

  接下來點開QueryString頁籤,檢視由GET方法傳送的資料:

             

Python爬蟲實戰---抓取圖書館借閱資訊

  需要傳送的資料包括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 + '?'      

  得到請求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':'114477yan'
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      

result = self.opener.open(request1) ,登入沒有異常,說明登入過程不用檢查資料頭;然後再用此 self.opener

​​​http://202.118.250.131/lib/opacAction.do?method=init&seq=301​​

                

Python爬蟲實戰---抓取圖書館借閱資訊

擷取借閱資訊:

  抓取了頁面資訊之後,接下來就是根據自己的需要比對、儲存資訊了。在比對頁面資訊時,這裡用的是正規表達式的方式進行比對,正規表達式的支援由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> ,式中,每一個 (.*?)

  在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      

擷取所需資料之後,接下來就是将資料寫入文本檔案(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      

  至此,整個爬蟲就算完成了,下面貼上完整代碼:

大功告成:  

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      

  下面就是抓到的借閱資訊,不得不說效果不怎麼樣,不過還是湊合着看把:

                

Python爬蟲實戰---抓取圖書館借閱資訊