资讯详情

鸿蒙教务查询实战:用JSoup解析课表与成绩并渲染ArkUI

📅 2026/10/7 13:11:55 | 华诺云谱 👁 阅读
鸿蒙教务查询实战:用JSoup解析课表与成绩并渲染ArkUI
简介这份资源是面向高校学生与鸿蒙开发初学者的课程设计完整工程围绕华为HarmonyOS平台结合Java的JSoup库实现教务信息查询功能可解决安卓应用向鸿蒙迁移、网页数据抓取与展示等实际问题。压缩包共183个文件约3.1MB以38个java源码、64个xml布局与配置、50个png图片资源为主另含12个json、7个jpg及gradle构建脚本、har包与jar依赖等覆盖工程配置、界面资源与核心逻辑。内容涉及鸿蒙开发环境搭建、JSoup集成与HTML解析、网络请求与HTTPS安全处理、课程表与成绩等数据提取展示、UI交互优化、分布式能力探索以及测试调试等环节ConnectJWGL等类体现了教务系统对接思路。已有450人学习适合作为课程设计参考或鸿蒙入门练手项目帮助读者理解跨平台开发流程与网页解析在鸿蒙中的落地方式。1. 鸿蒙教务查询软件用 JSoup 把课表和成绩抓进 HarmonyOS 应用很多同学做鸿蒙课程设计时第一反应是写个静态页面交差但真正能拿得出手、答辩时经得起追问的是能连上学校教务系统、把课表和成绩实时拉下来的工具类应用。这个标题讲的就是这么一件事在 HarmonyOS 应用里用 JSoup 这个 Java 生态里成熟的 HTML 解析库去请求教务系统页面、解析出成绩和课表数据再渲染到鸿蒙的 ArkUI 界面上。它解决的是「教务系统只有网页版、手机上体验差」这个真实痛点适合正在做鸿蒙课程设计、又想让作品有实际数据来源的开发者。核心难点不在界面而在网络请求、登录态维持和 HTML 结构解析这三块下面按落地顺序拆开讲。2. 为什么在鸿蒙里选 JSoup 而不是自己写正则2.1 JSoup 在 HarmonyOS 工程里的定位教务系统返回的是完整的 HTML 文档结构嵌套深、标签属性多用正则去抠成绩数字遇到页面改版或者标签顺序变化就会直接翻车。JSoup 提供的是 DOM 式的选择器语法select(table#scoreTable tr)这种写法跟前端用 CSS 选择器一样直观容错率比正则高一个量级。在鸿蒙工程里JSoup 是一个纯 Java 库不依赖 Android 运行时通过 Gradle 引入后可以在 ArkTS 的 Native 侧或者 Java 侧调用这也是它比很多 Android 专用解析库更适合鸿蒙的原因。选型上还有一个现实考量教务系统大多是十几年前的老系统页面编码可能是 GBK表格没有语义化标签JSoup 的charset指定和Element遍历能力刚好能覆盖这些脏数据场景。自己写解析器不是不行但课程设计的周期通常只有几周把时间花在解析轮子上不划算。2.2 用 Gradle 把 JSoup 依赖拉进鸿蒙工程鸿蒙的 Java 侧依赖管理走的是 Gradle和 Android 工程的配置思路一致但目录结构不同。下面是在entry模块的build.gradle里加依赖的最小改法// entry/build.gradle dependencies { // JSoup 核心库用于解析教务系统返回的 HTML implementation org.jsoup:jsoup:1.17.2 // 鸿蒙网络请求能力ohos 提供的 http 模块 implementation files(libs/ohos-network.jar) }逻辑说明implementation表示该依赖只在当前模块编译和运行时可见不会传递给上层模块适合工具类库。版本号选 1.17.x 是因为它对 Java 8 语法兼容好鸿蒙 Java 侧编译链对高版本 JSoup 的某些 API 支持不完整。参数上如果你的工程开了api而不是implementation依赖会暴露给调用方课程设计里没必要保持implementation即可。配置完执行一次 Gradle Sync如果卡在running gradle task assembleDebug很久大概率是仓库地址没配国内镜像或者 Gradle 离线包没下全。常见做法是在gradle-wrapper.properties里确认 distributionUrl 指向本地已缓存的版本避免每次构建都去拉远程。2.3 鸿蒙侧发起 HTTP 请求拿到 HTMLJSoup 本身只负责解析不负责发请求。鸿蒙里发请求要用ohos.net.http模块拿到响应字符串后再交给 JSoup。下面是一个封装好的请求方法// 使用鸿蒙 http 模块请求教务系统页面 import http from ohos.net.http; async function fetchHtml(url: string, cookie: string): Promisestring { let httpRequest http.createHttp(); let response await httpRequest.request(url, { method: http.RequestMethod.GET, header: { Cookie: cookie, // 维持登录态的关键 User-Agent: Mozilla/5.0 // 部分教务系统会校验 UA }, connectTimeout: 10000, // 连接超时 10 秒 readTimeout: 15000 // 读取超时 15 秒 }); httpRequest.destroy(); // 用完必须销毁否则连接池泄漏 return response.result as string; }逻辑说明createHttp()每次调用会创建一个请求对象destroy()是必须的否则在频繁查询成绩时会出现连接数耗尽。参数上connectTimeout设 10 秒是因为校园网到教务服务器通常延迟不高超过 10 秒基本是服务器挂了readTimeout设 15 秒是给教务系统慢查询留余量成绩页面在期末查分高峰期可能响应很慢。Cookie头是登录态的核心没有它请求会被重定向到登录页拿到的 HTML 里全是登录表单而不是成绩数据。3. 登录态维持与 HTML 解析的完整链路3.1 模拟登录拿到有效 Cookie教务系统登录一般分两步先 GET 登录页拿一个隐藏的 token 或者 session id再 POST 用户名密码。直接用 JSoup 解析登录页表单把隐藏字段一起提交是成功率最高的做法// 第一步GET 登录页解析隐藏字段 let loginPage await fetchHtml(loginUrl, ); let doc Jsoup.parse(loginPage); // 教务系统常见的隐藏字段名不同学校可能不同 let token doc.select(input[name__VIEWSTATE]).attr(value); let sessionId doc.select(input[nameJSESSIONID]).attr(value); // 第二步POST 登录表单 let formData username${user}password${pwd}__VIEWSTATE${token}; let loginResp await httpRequest.request(loginUrl, { method: http.RequestMethod.POST, header: { Content-Type: application/x-www-form-urlencoded }, extraData: formData }); // 从响应头 Set-Cookie 里提取 JSESSIONID let cookie loginResp.header[Set-Cookie];逻辑说明__VIEWSTATE是 ASP.NET 系教务系统的典型隐藏字段值每次刷新都变必须动态取。JSESSIONID是 Java 系教务系统的会话标识。参数上Content-Type必须是application/x-www-form-urlencoded用 JSON 提交会被服务端拒绝。提取 Cookie 时要注意Set-Cookie可能返回数组取第一个包含JSESSIONID的即可。3.2 用 JSoup 选择器解析成绩表格成绩页面通常是table结构每行一个课程列分别是课程名、学分、成绩。解析代码如下// 解析成绩表格 let scoreDoc Jsoup.parse(scoreHtml); // 选择所有数据行跳过表头 let rows scoreDoc.select(table.datelist tr); let scores []; for (let row of rows) { let cols row.select(td); if (cols.size() 4) continue; // 跳过表头或空行 scores.push({ courseName: cols.get(1).text().trim(), credit: cols.get(2).text().trim(), score: cols.get(3).text().trim() }); }逻辑说明table.datelist是教务系统常见的成绩表 class 名实际项目中需要先用浏览器开发者工具确认。cols.size() 4这个判断是为了过滤表头行和合并单元格产生的空行这是血泪经验——不加这个判断解析结果里会混入「课程名称 学分 成绩」这样的表头文字。参数上.text()会自动去除 HTML 标签并做实体转义.trim()去掉首尾空白两者配合能处理大部分脏数据。3.3 把解析结果渲染到 ArkUI 列表拿到scores数组后用 ArkUI 的List组件渲染。核心是把数据转成State装饰的状态列表项用ForEach遍历Entry Component struct ScorePage { State scores: ScoreItem[] []; build() { List() { ForEach(this.scores, (item: ScoreItem) { ListItem() { Row() { Text(item.courseName).layoutWeight(2) Text(item.credit).layoutWeight(1) Text(item.score).layoutWeight(1) } } }) } } }逻辑说明State装饰的变量变化时会触发 UI 重绘这是鸿蒙状态管理的基础。layoutWeight控制三列宽度比例课程名占两份学分和成绩各占一份。参数上ForEach的第二个参数是键生成函数课程设计里用索引即可生产环境建议用课程 ID 避免重复渲染。4. 避坑与排查教务查询最容易翻车的五个点4.1 请求返回登录页而不是数据页现象解析出来的 HTML 里全是input typepassword没有成绩表格。原因Cookie 没带上或者 Cookie 过期了。解决在每次请求前检查 Cookie 是否包含JSESSIONID没有就重新走登录流程另外确认header里的Cookie字段名大小写正确部分服务端对大小写敏感。4.2 JSoup 选择器返回空列表现象select(table.datelist tr)返回 size 为 0。原因教务系统实际 class 名不是datelist或者页面用了 iframe 嵌套。解决先把fetchHtml拿到的原始 HTML 打印出来用浏览器打开保存的 HTML 文件右键检查元素确认真实选择器如果是 iframe需要先解析出 iframe 的 src 再请求一次。4.3 中文乱码现象课程名显示成「课ç¨」这样的乱码。原因教务系统返回 GBK 编码但 JSoup 默认按 UTF-8 解析。解决在Jsoup.parse时指定编码Jsoup.parse(html, GBK)或者在 HTTP 请求头里加Accept-Charset: GBK。4.4 Gradle 构建卡在下载依赖现象running gradle task assembleDebug长时间无响应。原因Gradle 仓库指向国外源或者离线包不完整。解决在项目根目录build.gradle里把repositories改成国内镜像地址或者提前下载好 Gradle 离线包放到~/.gradle/wrapper/dists目录下。4.5 频繁请求被教务系统封 IP现象连续查询几次后所有请求都超时。原因教务系统有频率限制短时间大量请求会被临时封禁。解决在请求之间加setTimeout延迟课程设计里建议每次查询间隔 2 秒以上另外把查询结果缓存到本地避免重复请求同一页面。5. 进阶技巧用缓存和增量更新把查询速度压下来课程设计答辩时老师经常会问「如果教务系统挂了怎么办」。一个能加分的回答是本地缓存 增量更新。具体做法是每次成功查询后把成绩数据序列化成 JSON 存到鸿蒙的Preferences里下次打开应用先读缓存渲染再后台请求最新数据请求成功后再刷新 UI。这样即使教务系统临时不可用应用也能展示上次的数据不会白屏。缓存键的设计上建议用「学号 学期」作为 key因为不同学期的成绩是独立数据集。增量更新则是在解析成绩表格时用课程名 学分作为唯一标识跟缓存里的记录做对比只把新增的课程插入列表避免全量刷新导致的闪烁。验证缓存是否生效可以在fetchHtml里加一行日志打印请求耗时和是否命中缓存。我一般会在开发阶段把缓存过期时间设成 5 分钟方便测试上线前改成 24 小时减少对教务系统的压力。这个习惯帮我省了很多次因为教务系统维护导致的答辩翻车希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑