android超級課程表

超級課程表火了有一陣子,最近安裝體驗了下,對裏面自動導入課表這個功能很好奇——不清楚各大教學平臺網站的API情況下,怎麼獲得相應數據?

網上搜了下,找到這篇博文,講解很詳盡,做個收藏。


主要工作是抓包,然後再對數據報進行分析。

首先需要準備的工具是HttpWatch,這是抓包需要的工具,然後還有一個jar包,叫Jsoup,這是用來解析網頁HTML代碼的。其次所以要的類是HttpClient、HttpPost、HttpGet。


先來看看最後的效果圖,我實現了獲取教務平臺的考試成績和考試座位安排的數據,課程表數據一樣的原理獲取。


20130605124233937



關於android的基本知識及我所用的相應組件就不介紹了,直接開始正文了。


一、使用HttpWatch抓取教務平臺的數據。


安裝好HttpWatch後,打開IE瀏覽器,打開HttpWatch,先別點記錄,因爲還沒有進入教務平臺網站的。  = =

這是我學校的教務平臺網站地址 http://210.43.188.41/ ,進入後,選擇用戶登錄。好的,此時點擊HttpWatch上的記錄。

20130605125016078



然後輸入學號密碼, = = 這裏我就打上馬賽克了,如果開發者真的需要用我學校的教務平臺來進行學習,我願意給你我的學號密碼,不過請私下聯繫我安靜

輸入學號密碼後點擊登錄,等網頁完全加載完畢後點擊記錄邊上的取消,這個時候就要對抓下來的數據進行分析了。接下來的演示可能有點傻瓜制 = = 希望秒懂的人諒解一下像我這樣的新手。

大家可以看到HttpWatch有上下兩塊界面,首先看到上面的界面,找到“方法”爲Post的那行數據,單擊,就可以看到下面的界面出現了相應的內容。首先我們打開POST數據。


20130605125658718


大家可以看到有很多參數和數值,但是!!除了我打鉤的這三個參數外,其餘的參數對於我們開發客戶端而言形同虛設。大家可能會問了,在之前那個登陸頁面中,明明有驗證碼需要輸入的啊,但爲什麼在POST數據中,連cCode(驗證碼)這個參數都形同虛設呢?關於這個問題,我問了很多人,可是最終得到的結果是。。。。應該是這個教務平臺的BUG = =  所以大家先別介意沒有輸入驗證碼,我跟大家保證,我們不需要輸入驗證碼,也可以登錄!!!!得意


好的,在分析完POST數據後,我們點擊另一個選項卡,“頭信息”。

20130605130433140


同樣,在衆多發送的頭信息中,我們所需要的只是Cookie,Cookie是什麼?從本質上講,他可以看成你的×××,也就是說你在接下來的網頁操作中,Cookie可以證明操作對象是你而不是別人。好的,關於其餘參數的作用,如果你對抓包很有興趣的話可以繼續深究,但是對我們現在做客戶端已經無用了~安靜

對了,其實還有一個參數還是相當重要的,那就是在HttpWatch中上方的那個頁面中有一列叫做URL,這個就是我們Post或者Get的直接網址,一定要注意!!不然你Post的時候沒有Post到相對應的網站就等於白Post了 = =



第一步基本就已經完成了,就是關於使用HttpWatch抓包和分析數據的事基本就已經搞定了~(不過這只是第一個抓包內容,之後還需要抓包的,就是抓成績或者課表的頁面)。



二、將抓下來的數據運用在代碼中



  1. List<Cookie> cookies;                      //保存獲取的cookie

  2. HttpClient client = new DefaultHttpClient();                  

  3. HttpResponse httpResponse;    





  1. String uriAPI = "http://210.43.188.41/_data/index_LOGIN.aspx";  

  1. /* 建立HTTP Post連線 */

  2. HttpPost httpRequest = new HttpPost(uriAPI);  

  3. List<NameValuePair> params = new ArrayList<NameValuePair>();  

  1. /**

  2. * 以下三個數據就是我們的之前在POST裏的數據,不用在意驗證碼

  3. */

  4. params.add(new BasicNameValuePair("PassWord", "*****");   //這裏的密碼我用*取代了

  5. params.add(new BasicNameValuePair("UserID", "201150080223");    //這是學號

  6. params.add(new BasicNameValuePair("Sel_Type", "STU"));    //以學生身份登錄

  1. try {  

  2. // 發出HTTP request

  3.    httpRequest.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));  

  4. // 取得HTTP response

  5.    httpResponse = client.execute(httpRequest);   //執行

  6. // 若狀態碼爲200 ok

  7. if (httpResponse.getStatusLine().getStatusCode() == 200) {   //返回值正常

  8. // 獲取返回的cookie

  9.        cookies = ((AbstractHttpClient) client).getCookieStore().getCookies();  

  10.    } else {  

  11.    }  

  12. } catch (Exception e) {  

  13.    e.printStackTrace();  

  14. }  



上面的代碼應該還是比較易懂的,關於部分不熟悉的類請大家自行閱讀API文檔哈。

第二步的目的一是將三個數據(學號、密碼、登陸身份)Post到教務網站上,另一個是獲取到登陸成功後的cookie。


三、繼續抓包,不過這次是非常有針對性的抓包

之前已經提到了,我們之所以在登陸頁面進行了第一次抓包操作,完全只是爲了登陸成功並且獲取成功後的cookie,這樣,我們才能帶着cookie繼續訪問我們接下來想要訪問的東西。下面我以成績爲例子演示,課程表也是一樣的!!


在教務平臺上找到成績查詢界面並進入。

20130605132422375


同樣,先別記錄,你這時可以清除一下你之前抓下的數據 (= = 當然,如果你覺得不妨礙你分析抓下來的數據,你也可以不清除)。清除後,點擊HttpWatch的記錄,然後檢索,這時HttpWatch上又會出現很多很多數據。我們繼續分析。

同樣的,按照第一次抓包的方法,我們先找到POST數據的選項卡。

20130605132823046



這些就是我們剛纔Post的數據,反正這些數據我們大體上能理解,比如說sel_xn就是select的學年的意思,sel_xq就是學期,按道理來說這個數據我們都應該在代碼裏Post的,但是經過我自己的嘗試,第一個參數btn_search可以不用post,這個估計就是我們點擊“檢索”那下所產生的效果。好的,這個時候你可以去看看“頭信息”裏的cookie,你會發現這個cookie和我們之前的那個cookie是一樣的 = = 這是當然的,因爲這樣才能說明從剛開始到現在一直是同一個對象在操作。


第三步的作用很明顯,就是爲了POST一些數據到我們想要查詢的網頁上。


慢着,這裏一個非常非常非常重要的東西沒有講到!第三步其實還有一個作用的,那就是!!!!!大家點擊“POST數據”邊上的那個“內容”選項卡。


20130605133653625


看到沒?“內容”裏的數據原來就是我們網頁上成績的HTML源代碼!哈哈,大家懂了吧?其實抓包的目的很純粹的!就是爲了獲取相應的網頁信息。你像什麼QQ農場外掛啥的其實都是抓包的原理喲!


好的,這樣一來,我們大部分的事情就做完了,我們回憶一下之前的操作。

首先是在登陸頁面抓包,從而獲取相應的cookie,接着是在JAVA代碼中實現POST過程,然後我們再進行抓包操作,對查詢成績頁面進行的抓包,我們找到了我們所需要POST的數據和頁面所返回的內容,這樣我們就明朗了,我們只要合理的解析一下獲取的HTML代碼就可以了!不過在此之前,我們先把第三步的操作在JAVA代碼裏實現。


四、在代碼裏實現第三步操作



  1. String result = "";  




  1. /* 聲明網址字符串 */

  2.                        String uriAPI = "http://210.43.188.41/xscj/Stu_MyScore_rpt.aspx";  //這個網站之前說了,查看HttpWatch裏相應的URL  

  3. /* 建立HttpPost聯機 */

  4.                        HttpPost httpRequest = new HttpPost(uriAPI);  

  5.                        List<NameValuePair> params = new ArrayList<NameValuePair>();  

  6. /**

  7.                         * 以下六個參數必須要用

  8.                         */

  9.                        params.add(new BasicNameValuePair("sel_xn", "2011")); // 學年

  10.                        params.add(new BasicNameValuePair("SelXNXQ", "2"));  

  11.                        params.add(new BasicNameValuePair("SJ", "1"));  

  12.                        params.add(new BasicNameValuePair("sel_xq", term)); // 學期:0 第一學期,1 第二學期

  13.                        params.add(new BasicNameValuePair("zfx_flag", "0"));  

  14.                        params.add(new BasicNameValuePair("zfx", "0"));  

  15. try {<span style="font-family: Arial, Helvetica, sans-serif;">//把之前的cookie放到此次POST所需要的頭信息中</span>

  1. httpRequest.setHeader("Cookie","ASP.NET_SessionId="+ cookies.get(0).getValue());  

  2. httpRequest.setEntity(new UrlEncodedFormEntity(params2, HTTP.UTF_8));  

  3. /* 發出HTTP request */

  4. HttpResponse httpResponse2 = new DefaultHttpClient().execute(httpRequest3);  

  5. /* 若狀態碼爲200 ok */

  6. if (httpResponse2.getStatusLine().getStatusCode() == 200) {  

  1. <span style="white-space:pre">                              </span>//接下來的代碼是爲了把從網頁獲取到的內容讀出來

  2.                                StringBuffer sb = new StringBuffer();  

  3.                                HttpEntity entity = httpResponse2.getEntity();  

  4.                                InputStream is = entity.getContent();  

  5.                                BufferedReader br = new BufferedReader(new InputStreamReader(is, "GB2312"));  

  6. //是讀取要改編碼的源,源的格式是GB2312的,安源格式讀進來,然後再對源碼轉換成想要的編碼就行

  7.                                String data = "";  

  8. while ((data = br.readLine()) != null) {  

  9.                                    sb.append(data);  

  10.                                }  

  11.                                result = sb.toString();  //此時result中就是我們成績的HTML的源代碼了

  12.                            } else {  

  13.                            }  

  14.                        } catch (Exception e) {  

  15.                            e.printStackTrace();  

  16.                        }  



老規矩,對於一些大家不熟悉的類依舊自行查看API文檔。


五、使用Jsoup解析獲取的HTML代碼

好樣的,做完第四步的時候其實我們已經基本完成了百分之80的操作了,用Jsoup解析說難不難,說易不易,我這裏就只是把解析我所需要的內容進行一下講解。

先上代碼。



  1. private String filterHtml(String source) {  

  2. if (null == source) {  

  3. return"";  

  4.        }  

  5.        StringBuffer sff = new StringBuffer();  

  6.        String score[];  

  7. int i = 0, j = 0;  

  8.        String html = source;  

  9.        Document doc = Jsoup.parse(html);   //把HTML代碼加載到doc中

  10.        Elements links_class = doc.select("td[width=23%]"); // 這是課程名,因爲課程名的HTML標籤事<td width=23% align=left>,然後我發現<span style="font-family: Arial, Helvetica, sans-serif;">width=23%是這個標籤特有的,所以我就把它給提出來了</span>

  11.        Elements links_grade = doc.select("td[width=5%]");  // 這是分數,原因同上

  12.        score = new String[links_grade.size()];  

  13. for (Element link_grade : links_grade) {  

  14.            score[i++] = link_grade.text();  

  15.        }  

  16. for (Element link : links_class) {  

  17.            sff.append(link.text()).append(" : ").append(score[j]).append("\n");  

  18.            j = j+2;   //這裏之所以+2是因爲分數的標籤是<td width=5% align=right>,而學分也是這樣的標籤,所以我就每提取一次分數標籤跳過一次學分標籤

  19.        }  

  20.        html = sff.toString();  

  21. return html;  

  22.    }  



這樣函數中最後返回的html就是乾淨的代碼了。總之關於Jsoup的解析並不是重點,因爲解析HTML的方式還有很多的。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章