嵌入式系统课程设计:基于JSoup的鸿蒙教务查询软件

13 篇文章 5 订阅
7 篇文章 0 订阅
本文详细介绍了使用JSoup和鸿蒙开发构建的一款教务查询软件,用户无需校园网即可访问内网教务系统。软件包括成绩、考试、GPA和总表查询功能,以及个人中心模块,支持登录、修改个人信息和退出登录。通过JSoup模拟登录内网和教务系统,解析HTML获取数据并在鸿蒙应用中展示。此外,文章还展示了登录逻辑和关键代码片段。
摘要由CSDN通过智能技术生成

一、内容介绍

本次大作业主要使用JSoup和鸿蒙开发来实现一个教务查询软件。软件分为两个部分:教务查询和个人中心。用户只需要知道自己的学号、教务系统密码以及内网VPN密码,无论是否使用校园网,都能登录该应用。教务查询部分全部用JSoup实现并展示在鸿蒙手机应用中,该部分包括成绩查询、考试查询、GPA查询以及成绩总表查询四个功能。个人中心部分包含用户的个人信息以及退出登录等操作。

二、工具介绍

JSoup是一款Java的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。本次大作业利用JSoup来模拟登录进而爬取教务系统内的各种信息,获取到的成绩等信息将在鸿蒙手机应用上进行展示。

三、JSoup处理细节

1.需求分析

场景:需要访问教务系统,爬取出各种个人教务信息,并在自己所写的手机应用上进行展示。由于访问教务系统需要连接校园网,为了方便用户在校外也能使用本应用,所以本次爬取采用了“内网-教务系统”的两级爬取策略,即先模拟登录校园内网,然后携带内网cookies登录教务系统,最终爬取出相关信息。

2.模拟登录内网

内网登录界面如图1所示:
在这里插入图片描述
主要登录步骤如下:

  1. 在登录界面输入用户名以及登录密码,按下F12,并在Elements中搜索action,查找表单数据最终被提交到哪里,如图2所示:
    在这里插入图片描述
    可以看到,输入的表单数据最终被提交到了"/users/sign_in"里。
  2. 点击登录,在Network里面找到sign_in,可以看到模拟登录需要的各种信息,如图3所示:
    在这里插入图片描述
  3. 写代码模拟登录。

3.模拟登录教务系统

模拟登录进入到了内网界面后,点击教务系统,进入到教务系统的登录界面,利用同样的方式获取模拟登录所需的信息,登录到教务系统中。

四、界面

1.登录界面

登录界面如图4所示:
在这里插入图片描述
用户输入自己的学号、教务系统密码以及内网VPN密码后,点击Login按钮,即可成功登录。登录后,鸿蒙开发提供的工具包Preferences将自动保存用户的所有登录信息,信息存储在用户的SD卡中。当再次打开APP时,系统将自动登录。

2.查询主界面

查询主界面如图5所示:
在这里插入图片描述
在查询主界面,利用JSoup爬取到的网页信息设计了多个查询功能,下面将依次介绍。

2.1 成绩查询

点击查询主界面的“成绩查询”按钮,进入到成绩查询界面,点击学年、学期以及查询性质三个按钮,会弹出相应选项,如图6所示:
在这里插入图片描述
学年、学期以及课程性质都选择完毕后,点击查询按钮,结果如图7所示:
在这里插入图片描述
成绩信息中只包含课程名和成绩分数两项信息。点击其中任意一条成绩信息,随后会弹出该门课程更加详细的信息,如图8所示:
在这里插入图片描述
详细信息中包括课程性质、考试性质以及开课学院等信息。

2.2 考试安排

点击查询主界面的“考试安排”按钮,进入到考试安排查询界面,选择学年、学期选项,然后点击查询,结果如图9所示:
在这里插入图片描述
考试信息中只包括考试科目以及考试地点两项信息。任意点击其中一条信息,会弹出更加详细的考试信息,如图10所示:
在这里插入图片描述
详细信息中包括考试时间、教学班组成以及考试校区等信息。

2.3 GPA查询

点击查询主界面的“GPA”按钮,进入到GPA查询界面。选择学年、学期以及课程性质三个选项,然后点击查询,结果如图11所示:
在这里插入图片描述
可以看出该名学生2019-2020学年第2学期的GPA是4.37。

2.4 成绩总表

点击查询主界面的“成绩总表”按钮,进入到成绩总表查询界面,结果如图12所示:
在这里插入图片描述
成绩总表中包含该用户截止目前的所有学分以及课程的详细情况。

3.个人中心界面

个人中心界面显示了用户的个人信息,所有信息均通过JSoup爬取教务系统网页获得。当用户在登录界面点击登录后,获取到的用户个人信息将用于个人中心界面的初始化操作。用户登录成功后,个人中心界面如图13所示:
在这里插入图片描述

用户在个人中心界面可以完成查看个人信息、修改个性签名、查看作者博客、退出登录等操作。

3.1 个性签名

点击个人中心界面的“个性签名”按钮,进入到个性签名修改界面,如图14所示:
在这里插入图片描述
重新输入个性签名后,点击确定,即可成功修改。如果用户并未改变原有的内容,点击返回键可正常返回,否则系统会提示用户点击“确认”键来进行修改操作。修改后的个性签名如图15所示:
在这里插入图片描述

3.2 作者博客

点击个人中心界面的“作者博客”按钮,即可进入到开发者的CSDN博客主页,如图16所示:
在这里插入图片描述

3.3 退出登录

点击个人中心界面的“退出登录”按钮,会弹出一个提示窗口,如图17所示:
在这里插入图片描述

点击确认后,系统将清除掉用户的所有数据并跳转到登录界面(图4),重新进行登录。

五、附录

完整代码地址:代码,原创不易,请随手给个follow和star,感谢!!!

1.登录界面处理逻辑

package com.example.ncepu;

import com.example.ncepu.JWUtils.ConnectJWGL;
import com.example.ncepu.Utils.PreferenceUtils;
import com.example.ncepu.Utils.TimeUtils;
import com.example.ncepu.slice.MainAbilitySlice;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.Button;
import ohos.agp.components.TextField;
import ohos.agp.utils.Color;
import ohos.agp.window.dialog.ToastDialog;
import ohos.app.Context;
import ohos.data.DatabaseHelper;
import ohos.data.preferences.Preferences;

import java.util.Map;

public class MainAbility extends Ability {

    public static ConnectJWGL connectJWGL;
    private Button login;
    private TextField textId, textPwd1, textPwd2;
    private Preferences preferences;
    private DatabaseHelper databaseHelper;
    public static Context mcontext;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setMainRoute(MainAbilitySlice.class.getName());
        super.setUIContent(ResourceTable.Layout_ability_main);
        mcontext = getContext();
        initViews();
    }

    public void initViews() {
        login = (Button) findComponentById(ResourceTable.Id_main_btn_login);
        textId = (TextField) findComponentById(ResourceTable.Id_usersid);
        textPwd1 = (TextField) findComponentById(ResourceTable.Id_password);
        textPwd2 = (TextField) findComponentById(ResourceTable.Id_password1);
        login.setClickedListener(component -> {
            if(!TimeUtils.isFastClick()) {
                new ToastDialog(getContext()).setText("正在处理,请不要频繁点击!").show();
                return;
            }
            //设置颜色
            login.setTextColor(new Color(Color.getIntColor("#7adfb8")));
            String _id= textId.getText();
            String password=textPwd1.getText().toString();
            String password1 = textPwd2.getText().toString();
            if(_id.equals("")) {
                new ToastDialog(getContext()).setText("学号不能为空").show();
            }else if(password.equals("")) {
                new ToastDialog(getContext()).setText("教务系统密码不能为空!").show();
            }else if(password1.equals("")) {
                new ToastDialog(getContext()).setText("内网密码不能为空!").show();
            }else {
                new Thread(() -> {
                    try{
                        login(_id, password1, password);
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                }).start();//线程启动
            }
        });
    }

    private void login(String _id, String password1, String password) {
        try {
            connectJWGL = new ConnectJWGL(_id, password1, password, MainAbility.this);
            //内网登录密码错误,登录失败
            if(connectJWGL.init() == 0) {
                new ToastDialog(getContext()).setText("内网密码错误!").show();
//                runOnUiThread(() -> ToastUtil.showMessage(MainActivity.this, "内网密码错误!"));
            }else {
                if(MainAbility.connectJWGL.beginLogin()) {
                    Map<String, String> info_map = connectJWGL.getStudentInformation();
                    PreferenceUtils.getInstance().putString("id", _id);
                    PreferenceUtils.getInstance().putString("in_net", password1);
                    PreferenceUtils.getInstance().putString("sign", "Hungry And Humble");
                    PreferenceUtils.getInstance().putString("jw_password", password);
                    PreferenceUtils.getInstance().putString("name", info_map.get("name"));
                    PreferenceUtils.getInstance().putString("stu_id", info_map.get("id"));
                    PreferenceUtils.getInstance().putString("class", info_map.get("class"));
                    PreferenceUtils.getInstance().putString("major", info_map.get("major"));
                    PreferenceUtils.getInstance().putString("sex", info_map.get("sex"));
                    PreferenceUtils.getInstance().putString("dept", info_map.get("dept"));
                    PreferenceUtils.getInstance().putString("year", info_map.get("year"));

                    String cookies = connectJWGL.cookies.toString();
                    String cookies_in = connectJWGL.cookies_innet.toString();
                    PreferenceUtils.getInstance().putString("cookies", cookies);
                    PreferenceUtils.getInstance().putString("cookies_in", cookies_in);
//                    preferences.flush();
                    Intent intent = new Intent();
                    Operation operation = new Intent.OperationBuilder()
                            .withDeviceId("")
                            .withBundleName("com.example.ncepu")//这个必须填包名
                            .withAbilityName("com.example.ncepu.Student.StudentMainAbility")//这个必须填包名+类名
                            .build();
                    intent.setOperation(operation);
                    // 通过AbilitySlice的startAbility接口实现启动另一个页面
                    startAbility(intent);
//                    runOnUiThread(() -> ToastUtil.showMessage(MainActivity.this, "登录成功!"));

                }else {
                    new ToastDialog(getContext()).setText("教务系统密码错误!").show();
//                    runOnUiThread(() -> ToastUtil.showMessage(MainActivity.this, "教务系统密码错误!"));
                }
            }

        } catch (Exception e) {
//            runOnUiThread(() -> ToastUtil.showMessage(MainActivity.this, "内网连接超时,请检查网络是否正常连接或内网是否能正常访问!"));
            new ToastDialog(getContext()).setText("内网连接超时,请检查网络是否正常连接或内网是否能正常访问!").show();
            e.printStackTrace();
        }

    }
}

2.JSoup模拟登录

package com.example.ncepu.JWUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import com.example.ncepu.Utils.Exam;
import ohos.app.Context;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.util.*;
import com.example.ncepu.Utils.Grade;

public class ConnectJWGL {

    public Map<String,String> cookies_innet;
    Context mContext;
    private final String url = "***";
    public Map<String,String> cookies = new HashMap<>();
    private String modulus;
    private String exponent;
    private String csrftoken;
    private Connection connection;
    private Connection.Response response;
    private Document document;
    private String stuNum;
    private String jw_Password;
    private String in_Password;

    public ConnectJWGL(String stuNum, String in_Password, String jw_Password, Context mContext) throws Exception {
        this.stuNum = stuNum;
        this.in_Password = in_Password;
        this.jw_Password = jw_Password;
        this.mContext = mContext;
        Login_Innet login_innet = new Login_Innet(stuNum, in_Password);
        cookies_innet = login_innet.in_net();
    }
    //登录
    public boolean beginLogin() throws Exception{
        connection = Jsoup.connect(url+ "/jwglxt/xtgl/login_slogin.html").cookies(cookies_innet);
        //connection.header("Content-Type","application/x-www-form-urlencoded;charset=utf-8");
        connection.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36");
        connection.header("Connection", "keep-alive");

        connection.data("csrftoken",csrftoken);
//        connection.data("language", "zh_CN");
        connection.data("yhm",stuNum);
        connection.data("mm", jw_Password);
        connection.data("mm", jw_Password);

        response = connection.cookies(cookies).ignoreContentType(true).followRedirects(true)
                .method(Connection.Method.POST).timeout(60000).execute();
//        response = connection.execute();
        System.out.println("登录后的cookies为:" + response.cookies().get("JSESSIONID"));
        Map<String, String> subCookies = new HashMap<>();
        subCookies.put("JSESSIONID", response.cookies().get("JSESSIONID"));
        System.out.println("subCookies为:" + subCookies);
        cookies = subCookies;
        document = Jsoup.parse(response.body());
//        System.out.println("登录后的界面为:" + document);
        if(document.getElementById("tips") == null){
            System.out.println("教务系统密码正确");
            return true;
        }else{
            System.out.println(document.getElementById("tips").text());
            return false;
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cyril_KI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值