基于HttpClient的正方教务系统模拟登录(带验证码)
PS:恩,由于最近在學web和簡單的http協(xié)議,所以心血來潮想用java寫個爬蟲來爬取學校官網(wǎng)(正方教務系統(tǒng))個人主頁的基礎信息(課程信息、成績……),其實在之前學過java基礎教程的時候就可以寫的,但后知后覺總是在當前階段做之前階段應該完成的任務!或許這就是菜鳥吧~~~。
?
寫在前面:其實寫java爬蟲的話,一般使用三種方法:自帶庫類(urlconnection),外庫(httpclient)和文檔解析庫(jsoup);其中jsoup這個第三方庫重點用于對html文檔的解析,在官方文檔中除了Jsoup.connect()方法進行訪問網(wǎng)頁外并沒有多余的方法,而且在訪問網(wǎng)頁是需要登錄的網(wǎng)站爬蟲中(get和post方法訪問),大多使用前兩種方法。又由于httpclient相對于自帶庫更加靈活高效,所以在該爬蟲中使用httpclient訪問+Jsoup解析文檔結合操作。
?
注意:作為一個小白,在嘗試每一個別人已經(jīng)實現(xiàn)的而自己未曾實現(xiàn)的項目時,都會有或多或少的問題,而這個爬蟲項目對于我來說也是如此,所以才會寫下這篇博文作為填坑的記錄,也方便別的剛入門的小白能夠順利完成。如果正在閱讀的你也是一個剛入門的小白想用java寫一個爬取學校官網(wǎng)的爬蟲(大神勿噴請繞道~~~),那就很幸運了,只要你按照以下文章內容循序漸進,那一定會得到你想要的結果。(當然,在我接下來描述的項目中,是以正方教務系統(tǒng)來實現(xiàn)的,如果你的學校官網(wǎng)是其他的,其實掌握核心技術,也是大同小異)
?
- 前期準備:
- 項目環(huán)境:火狐瀏覽器,eclipseIDE
- 所需外庫jar包:
- commons-codec-1.3.jar
- commons-httpclient-3.1.jar
- commons-logging-1.0.4.jar
- jsouop-1.8.1.jar
注意:這幾個jar包是必不可少的,可能會有人疑惑:不是只要httpclient包就好了嘛,怎么還要其他兩個包。其實你只要知道這三個包的版本都是很老的,Apache已經(jīng)不再維護更新,而且這三個包是相互依賴的,必不可缺。(由于我一開始下載的就是3.1版本,不想再改4.X的,因為里面的一些方法函數(shù)的使用是不一樣的。下載的話我已經(jīng)都分享到csdn中,可以進行下載:httpclient-jar包下載),導入eclipse中的話就不用細說了,結果如下圖。
?
- 破土動工:
【注】爬蟲是什么,就不需要贅述了,如果是通過url爬取沒有權限的網(wǎng)頁是很簡單的,直接用Jsoup就可以完成。但是當我們在爬取一些需要登錄的網(wǎng)站時,我們如果還是直接訪問該url的話會重新返回到登錄首頁,所以我們就要進行模擬登錄,至于什么是模擬登錄,就是將賬號密碼驗證碼post給網(wǎng)站的服務器,通過記住其cookie來訪問登錄后的其他頁面(其實這是先行知識,之所以要提一嘴,是因為我在開始的時候也是一頭霧水狀態(tài))。
- 首先分析模擬登錄過程,先打開火狐瀏覽器,打開學校教務系統(tǒng)登錄界面。然后按F12打開抓包界面,按正常方式進行登錄。(正方教務系統(tǒng))
- 成功登錄以后接而分析登錄過程的http狀態(tài),你會發(fā)現(xiàn)其中有一個狀態(tài)碼為302的POST方法和200的GET方法,然后打開這兩個提交數(shù)據(jù),可以看見下圖。
?
其中紅框內就是請求數(shù)據(jù)界面的地址。
- 繼而分析提交數(shù)據(jù),在抓包分析的時候會發(fā)現(xiàn)其中有很多的參數(shù),其實就是在登錄的過程中瀏覽器會自動將你手動輸入的賬號密碼和驗證碼以及其自動生成的作為唯一身份驗證的cookie作為參數(shù)POST到網(wǎng)站的服務器,進行身份驗證,從而登錄。(至于cookie和session的區(qū)別以及POST和GET的區(qū)別下文會作解釋)參數(shù)如下圖:
在用代碼進行登錄網(wǎng)站時,是需要將這些表單數(shù)據(jù)作為參數(shù)提交的,但是雖然有很多參數(shù),但是真正需要的參數(shù)只需要其中標記的四個參數(shù):
- 學號:txtUserName
- 密碼:TextBox2
- 驗證碼:txtSecretCode
- _VIEWSTATE,該參數(shù)其實對于相同學校的官網(wǎng)都是一樣的,至于該參數(shù)在哪,在登錄網(wǎng)頁的html源碼中:
- 好了,需要提交的參數(shù)都已經(jīng)了解了,那么模擬登錄就可以實現(xiàn)了,但是,現(xiàn)在問題來了,就是登錄頁面時需要提交驗證碼,那驗證碼哪里獲取呢?如下圖:
?
?
所以每一次提交信息的時候就需要get該網(wǎng)頁獲取驗證碼,因為對于驗證碼的自動識別比較麻煩,所以在這個項目中時將驗證碼獲取下載到本地,再人工手動輸入該驗證碼,進行登錄。
【高能:關于驗證碼的自動識別,我在下一篇博文中會做出詳細解釋】
- 在成功登錄后就可以利用cookie訪問想訪問的頁面了。
?
- 總體框架已經(jīng)大致熟悉,接下來就直接上源碼:
?
?
- 結果顯示
- 源碼的話,就自己解讀吧,理解核心方法的話,就很容易理解代碼的意思,而且其中也添加了許多注釋,如果還有不懂得話,可以在評論里提問。
?
- 【注1】cookie和session的纏綿悱惻
??? 當你在瀏覽網(wǎng)站的時候,WEB 服務器會先送一些資料放在你的計算機上,Cookie會幫你在網(wǎng)站上所打的文字或是一些選擇,都記錄下來。當下次你再光臨同一個網(wǎng)站,WEB 服務器會先看看有沒有它上次留下的 Cookie 資料,有的話,就會依據(jù)Cookie里的內容來判斷使用者,送出特定的網(wǎng)頁內容給你。 Cookie 的使用很普遍,許多有提供個人化服務的網(wǎng)站,都是利用Cookie來辨認使用者,以方便送出使用者量身定做的內容,像是 Web 接口的免費 email 網(wǎng)站,都要用到 Cookie。
?
??? 具體來說cookie機制采用的是在客戶端保持狀態(tài)的方案,而session機制采用的是在服務器端保持狀態(tài)的方案。
同時我們也看到,由于采用服務器端保持狀態(tài)的方案在客戶端也需要保存一個標識,所以session機制可能需要借助于cookie機制來達到保存標識的目的,但實際上它還有其他選擇。
??? cookie機制。正統(tǒng)的cookie分發(fā)是通過擴展HTTP協(xié)議來實現(xiàn)的,服務器通過在HTTP的響應頭中加上一行特殊的指示以提示瀏覽器按照指示生成相應的cookie。然而純粹的客戶端腳本如JavaScript或者VBScript也可以生成cookie。而cookie的使用是由瀏覽器按照一定的原則在后臺自動發(fā)送給服務器的。瀏覽器檢查所有存儲的cookie,如果某個cookie所聲明的作用范圍大于等于將要請求的資源所在的位置,則把該cookie附在請求資源的HTTP請求頭上發(fā)送給服務器。
?
??? cookie的內容主要包括:名字,值,過期時間,路徑和域。路徑與域一起構成cookie的作用范圍。若不設置過期時間,則表示這個cookie的生命期為瀏覽器會話期間,關閉瀏覽器窗口,cookie就消失。這種生命期為瀏覽器會話期的cookie被稱為會話cookie。會話cookie一般不存儲在硬盤上而是保存在內存里,當然這種行為并不是規(guī)范規(guī)定的。若設置了過期時間,瀏覽器就會把cookie保存到硬盤上,關閉后再次打開瀏覽器,這些cookie仍然有效直到超過設定的過期時間。存儲在硬盤上的cookie可以在不同的瀏覽器進程間共享,比如兩個IE窗口。而對于保存在內存里的cookie,不同的瀏覽器有不同的處理方式
?
??? session機制。session機制是一種服務器端的機制,服務器使用一種類似于散列表的結構(也可能就是使用散列表)來保存信息。當程序需要為某個客戶端的請求創(chuàng)建一個session時,服務器首先檢查這個客戶端的請求里是否已包含了一個session標識(稱為sessionid),如果已包含則說明以前已經(jīng)為此客戶端創(chuàng)建過session,服務器就按照sessionid把這個session檢索出來使用(檢索不到,會新建一個),如果客戶端請求不包含sessionid,則為此客戶端創(chuàng)建一個session并且生成一個與此session相關聯(lián)的session id,sessionid的值應該是一個既不會重復,又不容易被找到規(guī)律以仿造的字符串,這個session id將被在本次響應中返回給客戶端保存。保存這個sessionid的方式可以采用cookie,這樣在交互過程中瀏覽器可以自動的按照規(guī)則把這個標識發(fā)送給服務器。一般這個cookie的名字都是類似于SEEESIONID。但cookie可以被人為的禁止,則必須有其他機制以便在cookie被禁止時仍然能夠把sessionid傳遞回服務器。
??? 經(jīng)常被使用的一種技術叫做URL重寫,就是把sessionid直接附加在URL路徑的后面。還有一種技術叫做表單隱藏字段。就是服務器會自動修改表單,添加一個隱藏字段,以便在表單提交時能夠把sessionid傳遞回服務器。比如:
<form name="testform"action="/xxx">
<input type="hidden"name="jsessionid"value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
<input type="text">
</form>
實際上這種技術可以簡單的用對action應用URL重寫來代替。
cookie 和session 的區(qū)別:
1、cookie數(shù)據(jù)存放在客戶的瀏覽器上,session數(shù)據(jù)放在服務器上。
2、cookie不是很安全,別人可以分析存放在本地的COOKIE并進行COOKIE操作,考慮到安全應當使用session。
3、session會在一定時間內保存在服務器上。當訪問增多,會比較占用你服務器的性能,考慮到減輕服務器性能方面,應當使用COOKIE。
4、單個cookie保存的數(shù)據(jù)不能超過4K,很多瀏覽器都限制一個站點最多保存20個cookie。
- 【注2】POST和GET的雙宿雙飛
- 從安全性看get<post,get提交數(shù)據(jù)會在瀏覽器的地址欄顯示
- 從提交的內容大小來看get<post,get提交的數(shù)據(jù)不能大于2k,而post提交的數(shù)據(jù)上理論不受限制,實際編程中建議不要大于64k
- 從請求響應速度來看get>post,get要求服務器立即處理請求,而post請求可能形成一個隊列請求。
?
- 記其中的三兩坑
【坑1】
- 在前期停滯了兩天,就是關于那個登錄界面的驗證碼和你訪問驗證碼網(wǎng)頁獲得的驗證碼是否一致的問題,我當時一直搞不懂如何保持相同,也搜了很多但是都是沒有說到點子上或者很多教程都是沒有在該點上側重注意的,所以這一點算是坑死我。下圖是當時搜的一些討論。后來還是經(jīng)過學長的指點才算是醍醐灌頂。
- 先簡單說說我當時的思考思路:由于我在POST參數(shù)時,需要將賬號密碼驗證碼和提交,這時是POST到loginURL,但是我有驗證碼的前提是要GET下checkCodeURL下載驗證碼,可是我怎么保證兩者的驗證碼就是同一驗證碼呢?
- 所以我想先GETcheckCodeURL獲取驗證碼,同時獲取該網(wǎng)頁的cookie,然后再用相同的cookie作為參數(shù)POST到loginURL,這樣的意思就是保證讓服務器知道我兩次訪問的是同一用戶,以保證兩個驗證碼保持一致,但是結果,當然毫無起色!!!!!那么問題出在那了?
- 看了這么多圖片,你應該能夠發(fā)現(xiàn)每個URL中都有和普通URL不同之處,如下圖,URL中有一長串字符編碼,【重點】其實這個就是對每個網(wǎng)頁的一個唯一標識碼,就是當不同次訪問網(wǎng)頁是服務器都會生成一個標識碼,只有當這個標識碼保持一致時,服務器才會認定兩個驗證碼是保持一致的。
?
【坑2】
- 其實解決完坑1后,你就會發(fā)現(xiàn)可以成功登錄了,但是這時當你想POST課程表頁面時,會出錯!WTF ?!
- 這時也是有三個參數(shù)要提交,和前面的類似,但是關鍵關鍵之處:一定要加該句,用于告訴該POST的網(wǎng)頁你從哪里來。
?
總結
以上是生活随笔為你收集整理的基于HttpClient的正方教务系统模拟登录(带验证码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [Unity3D]Unity3D游戏开发
- 下一篇: 基于JavaEE的水费管理系统_JSP网