Python爬蟲實戰---抓取圖書館借閱資訊,python爬蟲
Python爬蟲實戰---抓取圖書館借閱資訊
原創作品,引用請表明出處:Python爬蟲實戰---抓取圖書館借閱資訊
前段時間在圖書館借了很多書,借得多了就容易忘記每本書的應還日期,老是擔心自己會違約,影響日後借書,而自己又懶得總是登入到學校圖書館借閱系統查看,於是就打算寫一個爬蟲來抓取自己的借閱資訊,把每本書的應還日期給爬下來,並寫入txt檔案,這樣每次忘了就可以開啟該txt檔案查看,每次借閱資訊改變了,只要再重新運行一遍該程式,原txt檔案就會被新檔案覆蓋,裡面的內容得到更新。
用到的技術:
Python版本是 2.7 ,同時用到了urllib2、cookielib、re三個模組。urllib2用於建立請求(request),並抓取網頁資訊,返回一個類似於檔案類型的response對象;cookielib用於儲存cookie對象,以實現類比登入功能;re模組提供對Regex的支援,用於對抓取到的頁面資訊進行匹配,以得到自己想要的資訊。
抓取一個頁面:
使用urllib2簡單抓取一個網頁的過程非常簡單:
1 import urllib22 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 urllib22 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 cookielib2 cookie = coolielib.CookieJar()
建立cookie了就萬事大吉了嗎?沒那麼簡單,我們要完成的操作是發送登入請求、記錄cookie、再發送讀取借閱資訊的請求並向伺服器反饋cookie資訊,要完成這一系列的操作,原來的urlopen()方法已不能勝任,幸運的是,urllib2模組還提供了一個OpenerDirector類,可以接受一個cookie處理器為參數,實現上述功能,而這個cookie處理器則可以通過HTTPCookieProcessor類接受一個cookie對象執行個體化後得到。即先通過HTTPCookieProcessor執行個體化得到一個cookie處理器handler,再將此處理器headler作為參數傳入OpenDirector執行個體化得到一個能捕捉cookie資料的opener,代碼如下:
1 import urllib22 import cookielib3 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':'114477yan'15 })16 self.requestURL = self.loginURL + '?' + self.queryString17 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代碼,是部分列印結果:
擷取借閱資訊:
抓取了頁面資訊之後,接下來就是根據自己的需要匹配、儲存資訊了。在匹配頁面資訊時,這裡用的是Regex的方式進行匹配,Regex的支援由Python的Re模組提供支援,關於如何使用Regex,可以參考這裡:PythonRegex指南
使用Re模組進行匹配時,往往先將Regex字串編譯(compile)成一個Pattern執行個體,再利用Re模組中的re.findall(pattern , string),將字串string中和Regex匹配的資料以列表的形式返回。如果在pattern中有超過一個組(group),則返回的結果將是一個元組列表,如此Regex: <table.*?id="tb.*?width="50%"><font size=2>(.*?)</font>.*?<tr>.*?<tr>.*?<font size=2>(.*?)</font>.*?<font size=2>(.*?)</font>.*?</TABLE> ,式中,每一個 (.*?) 代表一個組,即此式中有3個組,則匹配時,返回一個元組列表,其中每一個元組又有3個資料。
在library類中,定義一個擷取資訊的方法(getInformation),以通過Regex匹配的方式擷取所需資料:
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':'114477yan'17 })18 self.requestURL = self.loginURL + '?' + self.queryString19 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爬蟲實戰---抓取圖書館借閱資訊