文章目錄
- 寫在前面
- 成果圖
- 開始干活
- 1、模擬登陸
- 2、爬取資訊
- 3、相關 UI 設計
- 總結一下
寫在前面
國慶在學校沒事干,正好某課程表的查成績功能又雙叕崩了,一怒之下把它卸載!(課程表功能推薦蘇大學長寫的 wakeup課程表,各大商店都有)
正好學了點 kotlin,開始了我的小白安卓開發之旅~
專案地址:Github:https://github.com/SukiEva/HHUGrades
APK下載:https://github.com/SukiEva/HHUGrades/releases
APK也可以在藍奏云下載:https://suki.lanzous.com/iIk7yh67eih 密碼:b9ez
歡迎 Star 和 Fork!
特別警告:
連接教務系統需在內網下,即連接校園網才能成功,
直接使用流量連接會卡死,請在校園網或校園VPN連接下使用該APP!
成果圖
![]() | ![]() |
![]() | ![]() |
(別問我為什么有的UI沒對齊,都是為了適配我自己的手機o(╥﹏╥)o
開始干活
宣告一下使用了哪些依賴,防止看代碼看不懂,很多操作通過已有的庫會簡化很多,
implementation 'org.jsoup:jsoup:1.13.1'
implementation "com.squareup.okhttp3:okhttp:4.9.0"
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation "org.jetbrains.anko:anko:$anko_version"
implementation 'androidx.gridlayout:gridlayout:1.0.0'
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4'
1、模擬登陸
模擬登陸和爬取資訊我之前用python的Requests庫寫過了,所以只是把相關換成了Java 的 okhttp 和 jsoup,
思路是通過okhttp請求驗證碼的鏈接,并記錄cookie,通過 bitmap 將圖片顯示在android上,
這部分我是看的 Android客戶端加載網站驗證碼(okHttp Jsoup)
網頁請求分析我就不介紹了,直接貼代碼:
// LoginActivity.kt
private fun loadingCaptchaPic() {
//client = OkHttpClient()
initJwxt()
client = OkHttpClient().newBuilder()
.cookieJar(object : CookieJar {
//cookie的快取區
private val cookieStore: HashMap<String, List<Cookie>> = HashMap()
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
//添加cookie
cookieStore[url.host] = cookies
cookie = cookies[0].name + "=" + cookies[0].value
}
override fun loadForRequest(url: HttpUrl): List<Cookie> {
val cookies = cookieStore[url.host]
//當Request 連接到網路的時候,OkHttp會呼叫loadForRequest()
// if (cookies != null) {
// println("加載了cookie:" + cookies)
// }
return cookies ?: ArrayList()
}
}).build()
val ImgUrl = homeUrl + "validateCodeAction.do"
//加載驗證碼圖片代碼
Thread(
object : Runnable {
var captchaPic: Bitmap? = null
override fun run() {
try {
val request = Request.Builder()
.removeHeader("User-Agent")
.addHeader(
"User-Agent",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4209.2 Safari/537.36"
)
.url(ImgUrl)
.build()
val response = client!!.newCall(request).execute()
val `is`: InputStream = response.body!!.byteStream()
captchaPic = BitmapFactory.decodeStream(`is`)
checkCodePicture?.post({ checkCodePicture!!.setImageBitmap(captchaPic) })
} catch (e: Exception) {
e.printStackTrace()
}
}
}).start()
}
因為我校有4個教務系統網址,有時候會隨機崩幾個,所以在請求之前還寫了個找到沒崩網址的方法:
// LoginActivity.kt
private fun initJwxt() {
val headerMap = mapOf(
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4209.2 Safari/537.36"
)
var pos = 0
var time = 0
while (time < 15) {
homeUrl = homeUrls[pos]
try {
Jsoup
.connect(homeUrl)
.headers(headerMap)
.ignoreContentType(true)
.ignoreHttpErrors(true)
.timeout(2000)
.execute()
return
} catch (e: Exception) {
pos++
if (pos > 3) pos = 0
} finally {
time++
}
}
if (time >= 15) {
alert("教務系統崩潰啦!!![○・`Д′・ ○]") { positiveButton("?( 'ω' )? get!") {} }
}
return
}
驗證碼圖片搞定,然后就是 Post 過去就行了,okhttp開啟cookie后會自動保持,我們直接用同一個 client 就行:
(細心就會發現我里面還放了處理資訊的函式,會在下面介紹~)
// LoginActivity.kt
private fun ButtonClickHandler() {
loginNum = findViewById<EditText>(R.id.loginNum).text.toString()
loginPassword = findViewById<EditText>(R.id.loginPassword).text.toString()
yzm = findViewById<EditText>(R.id.loginYzm).text.toString()
when {
loginNum.equals("") -> {
alert("請填寫學號! ̄へ ̄") { positiveButton("?( 'ω' )? get!") {} }.show()
return
}
loginPassword.equals("") -> {
alert("請填寫密碼!(* ̄︿ ̄)") { positiveButton("?( 'ω' )? get!") {} }.show()
return
}
yzm.equals("") -> {
alert("請填寫驗證碼!凸(艸皿艸 )") { positiveButton("?( 'ω' )? get!") {} }.show()
return
}
}
// android sharedpreferences 保存賬號密碼,可略過~
if (rembox!!.isChecked) {
val editor = sp!!.edit()
editor.putString("uname", loginNum)
editor.putString("upswd", loginPassword)
editor.putBoolean("checkboxBoolean", true)
editor.commit()
} else {
val editor = sp!!.edit()
editor.putString("uname", null)
editor.putString("upswd", null)
editor.putBoolean("checkboxBoolean", false)
editor.commit()
}
val LoginUrl = homeUrl + "loginAction.do"
val requestbody: RequestBody = FormBody.Builder()
.add("zjh", loginNum)
.add("mm", loginPassword)
.add("v_yzm", yzm)
.build()
try {
val request = Request.Builder()
.removeHeader("User-Agent")
.addHeader(
"User-Agent",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4209.2 Safari/537.36"
)
.url(LoginUrl)
.post(requestbody)
.build()
val response = client!!.newCall(request).execute()
val homehtml = response.body?.string()
if (homehtml!!.contains("學分制綜合教務")) {
val grades = GetGrades()
val rank = GetRank()
val datas = Datas(grades, rank)
val intent:Intent
if (this.flag)
intent = Intent(this, ShowResultsActivity::class.java)
else
intent = Intent(this, ShowRankActivity::class.java)
intent.putExtra("datas", datas)
indeterminateProgressDialog("登錄中")
startActivity(intent)
finish()
} else {
alert("要不重試一下?…(⊙_⊙;)…") {
title = "登錄失敗??????"
positiveButton("?( 'ω' )? get!") {}
}.show()
findViewById<EditText>(R.id.loginYzm).setText("")
loadingCaptchaPic()
}
} catch (e: Exception) { // 一般就是登錄失敗~
e.printStackTrace()
alert("要不重試一下?…(⊙_⊙;)…") {
title = "登錄失敗??????"
positiveButton("?( 'ω' )? get!") {}
}.show()
findViewById<EditText>(R.id.loginYzm).setText("")
loadingCaptchaPic()
}
}
相關 Layout 代碼建議查看原始碼,我貼一大堆也沒人想看~
2、爬取資訊
推薦另一個 獲取正方教務系統成績文章,我參考了一下,適配 URP 系統~
主要獲取成績資訊和排名資訊,同理 請求分析自行解決~
// LoginActivity.kt
fun GetGrades(): MutableList<List<String>>? {
val GradesUrl = homeUrl + "bxqcjcxAction.do"
try {
val courses: MutableList<List<String>> = mutableListOf()
val request = Request.Builder()
.removeHeader("User-Agent")
.addHeader(
"User-Agent",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4209.2 Safari/537.36"
)
.url(GradesUrl)
.build()
val response = client!!.newCall(request).execute()
val gradeshtml = response.body?.string()
val parse = Jsoup.parse(gradeshtml)
val trs = parse.getElementsByClass("odd")
val tds = trs.tagName("td")
val regex = Regex("[a-z\"]+", RegexOption.IGNORE_CASE)
val regex2 = Regex("\\s+")
for (td in tds) {
val str = td.text().replace(regex, "")
val course: MutableList<String> = str.split(regex2).toMutableList()
val ncouse: MutableList<String> = mutableListOf()
if (course.size >= 11) course.removeAt(3)
ncouse.add(course[2])
ncouse.add(course[8])
ncouse.add("課程屬性:" + course[4])
ncouse.add("學分:" + course[3])
courses.add(ncouse)
}
return courses
} catch (e: Exception) {
e.printStackTrace()
return null
}
}
fun GetRank(): MutableList<String>? {
val RankUrl = homeUrl + "reportFiles/bzrcx/jdpmcx.jsp?temp=1"
try {
val rankinfos: MutableList<String> = mutableListOf()
val request = Request.Builder()
.removeHeader("User-Agent")
.addHeader(
"User-Agent",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4209.2 Safari/537.36"
)
.url(RankUrl)
.build()
val response = client!!.newCall(request).execute()
val rankhtml = response.body?.string()
val parse = Jsoup.parse(rankhtml)
val infos = parse.getElementsByClass("report1_1_3_1")
for (info in infos) {
rankinfos.add(info.text())
}
return rankinfos
} catch (e: Exception) {
e.printStackTrace()
return null
}
}
3、相關 UI 設計
自己隨便寫了些,想美化的自己修改,代碼請去Github查看~
總結一下
資料獲取隨便寫,安卓Layout設計寫死人,珍愛生命,遠離安卓!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/157308.html
標籤:其他




