手把手帶你打造一個(gè)教務(wù)系統(tǒng)客戶(hù)端(附源碼)

本篇博客主要和大家分享編寫(xiě)一個(gè)學(xué)校教務(wù)系統(tǒng)的客戶(hù)端版本,主要是關(guān)于登錄以及數(shù)據(jù)獲取方面,結(jié)尾還會(huì)附上本人以前編寫(xiě)的客戶(hù)端源代碼,有興趣的可以自行下載玩耍~
閱讀本文大概需要5分鐘。

前言

好久沒(méi)有更新博客了,最近有點(diǎn)忙。今天對(duì)之前在學(xué)校做的一個(gè)項(xiàng)目開(kāi)源,并以正方教務(wù)系統(tǒng)為例,分享下如何抓取教務(wù)系統(tǒng)的數(shù)據(jù)~ 好了廢話(huà)不多說(shuō)直接開(kāi)始。

分析

搭建一個(gè)App,首先離不開(kāi)的肯定就是數(shù)據(jù),在通常情況下,App的數(shù)據(jù)都是由服務(wù)器提供的接口返回的,但是一般來(lái)說(shuō),學(xué)校都是不會(huì)把數(shù)據(jù)以及服務(wù)器提供給學(xué)生的,所以就要采取一些非正常手段。我們知道,網(wǎng)頁(yè)是由瀏覽器解析html代碼后展現(xiàn)出來(lái)的,那么只要我們拿到html代碼,自己抓取html里我們所需要的數(shù)據(jù),就能完成對(duì)數(shù)據(jù)的獲取了。
這里我使用的是一個(gè)能方便處理html文本的java庫(kù)Jsoup,對(duì)于它的具體用法可以參考我之前的文章《Android利用Jsoup抓取數(shù)據(jù),再也不怕寫(xiě)App沒(méi)有數(shù)據(jù)啦》,這里就不再贅述了。

登錄

Cookie保存

通常我們使用瀏覽器去訪(fǎng)問(wèn)我們的教務(wù)系統(tǒng)的時(shí)候,服務(wù)器都是通過(guò)cookie來(lái)對(duì)我們當(dāng)前的狀態(tài)進(jìn)行判斷以便獲取我們的登錄狀態(tài),那么為了能讓我們的登錄狀態(tài)得以持續(xù),以便我們后續(xù)對(duì)其他數(shù)據(jù)的抓取,我們?cè)诳蛻?hù)端中需要對(duì)cookie進(jìn)行一下存儲(chǔ)。

因?yàn)槲也捎玫氖荗kHttp來(lái)作為網(wǎng)絡(luò)請(qǐng)求,所以這里以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>();
    }
}

這里我只將其存入到了一個(gè)map中,并沒(méi)有對(duì)cookie進(jìn)行持久化存儲(chǔ)(比如通過(guò)SharedPreferences)等等,所以意味著每次重新打開(kāi)客戶(hù)端都需要登錄一遍,大家可以根據(jù)自己的需求進(jìn)行改造。

模擬登錄

首先我們需要先抓取到登錄的接口,以Chrome為例,按F12打開(kāi)開(kāi)發(fā)者工具,然后選擇Network,勾選Preserve log。

DevTools

然后進(jìn)行一次正常的登錄,就可以抓取到登錄的url以及請(qǐng)求頭,表單數(shù)據(jù)等等(圖片對(duì)一些敏感數(shù)據(jù)做了處理)。

登錄

可以看到請(qǐng)求頭以及表單所需要的內(nèi)容,根據(jù)你所填的賬號(hào)密碼驗(yàn)證碼等等,很快就能判斷出對(duì)應(yīng)的key,以我之前學(xué)校為例的話(huà),TextBox1對(duì)應(yīng)賬號(hào),TextBox2對(duì)應(yīng)密碼,TextBox3對(duì)應(yīng)驗(yàn)證碼,RadioButtonList1就是身份了,然后你肯定發(fā)現(xiàn)了,_VIEWSTATE是什么鬼,因?yàn)檫@個(gè)正方教務(wù)系統(tǒng)是用Asp.net寫(xiě)的,那個(gè)_VIEWSTATE就是.net的,這里我們不探究它到底做啥用的,據(jù)我觀(guān)察,這個(gè)值并不是永遠(yuǎn)不變的,所以這里我們肯定是要在每次登錄的時(shí)候獲取它并把它放到表單里,那從哪里獲取它呢。還是一樣,F(xiàn)12然后查看登錄頁(yè)面的html源碼,

html

可以發(fā)現(xiàn)這個(gè)_VIEWSTATE的變量值就存在于form表單中,那么一切都很簡(jiǎn)單了,先獲取一次登錄頁(yè)面,拿到了_VIEWSTATE的值之后,在登錄的時(shí)候?qū)⑦@個(gè)值一起post上去就可以了。即為拿到登錄頁(yè)面的html源碼,使用Jsoup篩選出需要的值,然后登錄的時(shí)候一并post上去

String __VIEWSTATE = Jsoup.parse(html).select("input[name='__VIEWSTATE']").val();

這里不再贅述Jsoup的具體用法,可以參考我之前的文章。以O(shè)kHttp為例,附上簡(jiǎn)單的登錄代碼

 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 {

            }
        });

整個(gè)登錄流程如下


登錄

關(guān)于驗(yàn)證碼,這里要補(bǔ)充一點(diǎn),即請(qǐng)求驗(yàn)證碼圖片的cookie要和你登錄的時(shí)候一致,驗(yàn)證碼才能通過(guò),從代碼角度來(lái)說(shuō),以O(shè)kHttp為例,你需要用同一個(gè)OkHttp對(duì)象去完成請(qǐng)求驗(yàn)證碼以及登錄等等(就是不要new 兩個(gè)對(duì)象啦)~

抓取數(shù)據(jù)

登錄成功后,我們現(xiàn)在已經(jīng)能夠拿到各個(gè)模塊的數(shù)據(jù)了,那么一切都好辦了。具體怎么拿這里以獲取課表為例,同理其他的獲取成績(jī)等等均是這個(gè)思路

首頁(yè)

正方教務(wù)系統(tǒng)的首頁(yè)一般都是這個(gè)樣子的,我們老規(guī)矩,F(xiàn)12查看一下html源碼

html

可以看到,各個(gè)模塊的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集合中,因?yàn)閔tml中的url是在同個(gè)域下的,所以抓取出來(lái)的url是不包含域名的,這里我們手動(dòng)把它拼上就可以了,現(xiàn)在我們拿到對(duì)應(yīng)模塊的url,還是老套路,按照所需要的參數(shù)進(jìn)行訪(fǎng)問(wèn),拿到html源碼

html

按照規(guī)則使用Jsoup進(jìn)行解析就行了,這里就不再贅述了,最后效果如下

demo

總結(jié)

因?yàn)槠鶈?wèn)題,所以本文難以很細(xì)致的講清楚整個(gè)項(xiàng)目的每個(gè)細(xì)節(jié),只能大概的將整個(gè)思路分享出來(lái),如果有興趣的也可以自行clone源碼進(jìn)行查看,為了方便大家查看demo的效果,我在demo里已經(jīng)放入了一些html靜態(tài)頁(yè)面,不用賬號(hào)密碼即可直接登錄
源碼地址:教務(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é)校對(duì)應(yīng)的url

3.運(yùn)行App,看哪里解析有問(wèn)題,針對(duì)你學(xué)校教務(wù)系統(tǒng)的html代碼,根據(jù)css樣式等差異進(jìn)行微調(diào)。


如果覺(jué)得對(duì)你有所幫助,請(qǐng)點(diǎn)個(gè)贊,謝謝。你的鼓勵(lì)是我最大的動(dòng)力。
歡迎關(guān)注EoniJJ的簡(jiǎn)書(shū)

不定期與你分享關(guān)于Android開(kāi)發(fā)的點(diǎn)點(diǎn)滴滴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,619評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,155評(píng)論 3 425
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 177,635評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,539評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,255評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,646評(píng)論 1 326
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,655評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,838評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,399評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,146評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,338評(píng)論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,893評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,565評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,983評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,257評(píng)論 1 292
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,059評(píng)論 3 397
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,296評(píng)論 2 376

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,701評(píng)論 25 708
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,814評(píng)論 18 139
  • 前言 在Android開(kāi)發(fā)中,網(wǎng)絡(luò)請(qǐng)求十分常用 而在Android網(wǎng)絡(luò)請(qǐng)求庫(kù)中,Retrofit是當(dāng)下最熱的一個(gè)網(wǎng)...
    Carson帶你學(xué)安卓閱讀 70,867評(píng)論 48 393
  • 二零一七 來(lái)到三月 不知不覺(jué) 已沒(méi)太多 結(jié)束傀儡的歲月進(jìn)入自己的春天 富足的墮落的思考著 今天的明天的時(shí)間呢 我沒(méi)...
    騎著小車(chē)聽(tīng)音樂(lè)閱讀 315評(píng)論 0 1
  • 沿著公路上的白線(xiàn),躲過(guò)地上的井蓋,耳邊聽(tīng)著人造風(fēng)。10月2日早上七點(diǎn)零幾分,我騎著電車(chē)前往老董的住處,那天我們計(jì)劃...
    和規(guī)則決裂閱讀 530評(píng)論 0 1