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爬蟲實戰---抓取圖書館借閱信息

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM