本篇博客主要和大家分享編寫一個學(xué)校教務(wù)系統(tǒng)的客戶端版本哩至,主要是關(guān)于登錄以及數(shù)據(jù)獲取方面,結(jié)尾還會附上本人以前編寫的客戶端源代碼辜妓,有興趣的可以自行下載玩耍~
閱讀本文大概需要5分鐘罗洗。
前言
好久沒有更新博客了,最近有點忙个曙。今天對之前在學(xué)校做的一個項目開源,并以正方教務(wù)系統(tǒng)為例受楼,分享下如何抓取教務(wù)系統(tǒng)的數(shù)據(jù)~ 好了廢話不多說直接開始垦搬。
分析
搭建一個App,首先離不開的肯定就是數(shù)據(jù)艳汽,在通常情況下猴贰,App的數(shù)據(jù)都是由服務(wù)器提供的接口返回的,但是一般來說河狐,學(xué)校都是不會把數(shù)據(jù)以及服務(wù)器提供給學(xué)生的米绕,所以就要采取一些非正常手段。我們知道馋艺,網(wǎng)頁是由瀏覽器解析html代碼后展現(xiàn)出來的栅干,那么只要我們拿到html代碼,自己抓取html里我們所需要的數(shù)據(jù)捐祠,就能完成對數(shù)據(jù)的獲取了碱鳞。
這里我使用的是一個能方便處理html文本的java庫Jsoup,對于它的具體用法可以參考我之前的文章《Android利用Jsoup抓取數(shù)據(jù)踱蛀,再也不怕寫App沒有數(shù)據(jù)啦》窿给,這里就不再贅述了。
登錄
Cookie保存
通常我們使用瀏覽器去訪問我們的教務(wù)系統(tǒng)的時候率拒,服務(wù)器都是通過cookie來對我們當(dāng)前的狀態(tài)進(jìn)行判斷以便獲取我們的登錄狀態(tài)崩泡,那么為了能讓我們的登錄狀態(tài)得以持續(xù),以便我們后續(xù)對其他數(shù)據(jù)的抓取猬膨,我們在客戶端中需要對cookie進(jìn)行一下存儲角撞。
因為我采用的是OkHttp來作為網(wǎng)絡(luò)請求,所以這里以O(shè)kHttp為例
OkHttpClient okHttpClient = new OkHttpClient.Builder().
connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS).
readTimeout(READ_TIMEOUT, TimeUnit.SECONDS).
writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS).
cookieJar(new OkHttpCookieJar()).
build();
public class OkHttpCookieJar implements CookieJar {
private Map<String, List<Cookie>> cookieStore = new HashMap<>();
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
cookieStore.put(url.host(), cookies);
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
List<Cookie> cookies = cookieStore.get(url.host());
return cookies != null ? cookies : new ArrayList<Cookie>();
}
}
這里我只將其存入到了一個map中,并沒有對cookie進(jìn)行持久化存儲(比如通過SharedPreferences)等等靴寂,所以意味著每次重新打開客戶端都需要登錄一遍磷蜀,大家可以根據(jù)自己的需求進(jìn)行改造。
模擬登錄
首先我們需要先抓取到登錄的接口百炬,以Chrome為例褐隆,按F12打開開發(fā)者工具,然后選擇Network剖踊,勾選Preserve log庶弃。
然后進(jìn)行一次正常的登錄,就可以抓取到登錄的url以及請求頭德澈,表單數(shù)據(jù)等等(圖片對一些敏感數(shù)據(jù)做了處理)歇攻。
可以看到請求頭以及表單所需要的內(nèi)容,根據(jù)你所填的賬號密碼驗證碼等等梆造,很快就能判斷出對應(yīng)的key缴守,以我之前學(xué)校為例的話,TextBox1對應(yīng)賬號镇辉,TextBox2對應(yīng)密碼屡穗,TextBox3對應(yīng)驗證碼,RadioButtonList1就是身份了忽肛,然后你肯定發(fā)現(xiàn)了村砂,_VIEWSTATE是什么鬼,因為這個正方教務(wù)系統(tǒng)是用Asp.net寫的屹逛,那個_VIEWSTATE就是.net的础废,這里我們不探究它到底做啥用的,據(jù)我觀察罕模,這個值并不是永遠(yuǎn)不變的评腺,所以這里我們肯定是要在每次登錄的時候獲取它并把它放到表單里,那從哪里獲取它呢手销。還是一樣歇僧,F(xiàn)12然后查看登錄頁面的html源碼图张,
可以發(fā)現(xiàn)這個_VIEWSTATE的變量值就存在于form表單中锋拖,那么一切都很簡單了,先獲取一次登錄頁面祸轮,拿到了_VIEWSTATE的值之后兽埃,在登錄的時候?qū)⑦@個值一起post上去就可以了。即為拿到登錄頁面的html源碼适袜,使用Jsoup篩選出需要的值柄错,然后登錄的時候一并post上去
String __VIEWSTATE = Jsoup.parse(html).select("input[name='__VIEWSTATE']").val();
這里不再贅述Jsoup的具體用法,可以參考我之前的文章。以O(shè)kHttp為例售貌,附上簡單的登錄代碼
RequestBody requestBody = new MultipartBody.Builder().
addFormDataPart("__VIEWSTATE", __VIEWSTATE ).
addFormDataPart("TextBox1", username).
addFormDataPart("TextBox2", password).
addFormDataPart("TextBox3", verificationCode).
addFormDataPart("Button1", "").
addFormDataPart("RadioButtonList1", "學(xué)生").build();
Request request = new Request.Builder().url(loginUrl).post(requestBody).build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
整個登錄流程如下
關(guān)于驗證碼给猾,這里要補充一點,即請求驗證碼圖片的cookie要和你登錄的時候一致颂跨,驗證碼才能通過敢伸,從代碼角度來說,以O(shè)kHttp為例恒削,你需要用同一個OkHttp對象去完成請求驗證碼以及登錄等等(就是不要new 兩個對象啦)~
抓取數(shù)據(jù)
登錄成功后池颈,我們現(xiàn)在已經(jīng)能夠拿到各個模塊的數(shù)據(jù)了,那么一切都好辦了钓丰。具體怎么拿這里以獲取課表為例躯砰,同理其他的獲取成績等等均是這個思路
正方教務(wù)系統(tǒng)的首頁一般都是這個樣子的,我們老規(guī)矩携丁,F(xiàn)12查看一下html源碼
可以看到琢歇,各個模塊的url均能拿到,老規(guī)矩梦鉴,直接拿到源碼矿微,Jsoup解析一下
Map<String, String> urlMap = new HashMap<>();
Document document = Jsoup.parse(html);
Elements elements = document.select("ul.nav li.top ul.sub li a");
for (Element element : elements) {
String value = "教務(wù)網(wǎng)的host" + "/" + element.attr("href").toString();
String key = element.text();
urlMap.put(key, value);
}
return urlMap;
這里我直接保存到map集合中,因為html中的url是在同個域下的尚揣,所以抓取出來的url是不包含域名的涌矢,這里我們手動把它拼上就可以了,現(xiàn)在我們拿到對應(yīng)模塊的url快骗,還是老套路娜庇,按照所需要的參數(shù)進(jìn)行訪問,拿到html源碼
按照規(guī)則使用Jsoup進(jìn)行解析就行了方篮,這里就不再贅述了名秀,最后效果如下
總結(jié)
因為篇幅問題,所以本文難以很細(xì)致的講清楚整個項目的每個細(xì)節(jié)藕溅,只能大概的將整個思路分享出來匕得,如果有興趣的也可以自行clone源碼進(jìn)行查看,為了方便大家查看demo的效果巾表,我在demo里已經(jīng)放入了一些html靜態(tài)頁面汁掠,不用賬號密碼即可直接登錄。
源碼地址:教務(wù)管理系統(tǒng)
關(guān)于快速替換為自己學(xué)校的教務(wù)系統(tǒng)
如果你學(xué)校的教務(wù)系統(tǒng)也是正方集币,那么這里提供一下比較快速的替換方法考阱,但可能由于css樣式等差異,具體可能還是需要微調(diào)鞠苟,就需要你根據(jù)你學(xué)校教務(wù)系統(tǒng)的html源碼進(jìn)行調(diào)整了乞榨。
1.首先秽之,CommonUtils.java中的isDemo改為false
public class CommonUtils {
public static boolean isDemo = true; // 改為false
....
}
2.將/app/src/main/res/values/api.xml下的url替換為你學(xué)校對應(yīng)的url
3.運行App,看哪里解析有問題吃既,針對你學(xué)校教務(wù)系統(tǒng)的html代碼考榨,根據(jù)css樣式等差異進(jìn)行微調(diào)。
如果覺得對你有所幫助鹦倚,請點個贊董虱,謝謝。你的鼓勵是我最大的動力申鱼。
歡迎關(guān)注EoniJJ的簡書
不定期與你分享關(guān)于Android開發(fā)的點點滴滴愤诱。