栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

基于SpringBoot+JSoup+POI+Swagger2实现校园教务系统成绩课程等信息抓取,并提供接口访问的小项目

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于SpringBoot+JSoup+POI+Swagger2实现校园教务系统成绩课程等信息抓取,并提供接口访问的小项目

介绍

教务系统(强智系统)、图书馆系统常用接口,可查询学生信息、学生课程信息、成绩信息、素拓修学情况、绩点情况、考试时间、图书馆推荐书目、搜索图书。实现了强智教务系统的模拟登录,网页解析,返回json数据格式。

项目地址
Github码云
https://github.com/waiterxiaoyy/waiter-gdufe-apihttps://gitee.com/waiterxiaoyy/gdufe-api
参考接口文档

https://docs.apipost.cn/preview/aadd819acb856ef2/66465aa51a4e56f1

你也可以在启动项目后访问:http://localhost:9093/swagger-ui.html

项目介绍 教务系统登录

解决抓取教务系统信息最主要的就是登录问题,只要获得登录成功后的cookie,然后携带cookie去访问其他页面,再对页面进行解析即可,这里描述模拟登录的过程,首先是获取学号和密码,然后获取验证码,由于验证码是图片,所以需要对图片进行解析,这里采用二值化的方式,获得四位的code,再通过jsoup携带cookie、学号、密码、code模拟登录。

public class InitLogin {

    private String url_Login = "http://jwxt.gdufe.edu.cn/jsxsd/xk/LoginToXkLdap"; // 登录
    private String url_safecode = "http://jwxt.gdufe.edu.cn/jsxsd/verifycode.servlet"; // 验证码


    //学号,密码
    private String username;
    private String password;

    private GetSafeCode getSafeCode = new GetSafeCode();
    public Map cookie;
    public byte[] bytes;
    

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    
    public void getcookie() throws IOException {
        Connection.Response response = Jsoup.connect(url_safecode).ignoreContentType(true) // 获取图片需设置忽略内容类型
                .userAgent("Mozilla").method(Connection.Method.GET).timeout(3000).execute();
        cookie = response.cookies();
        bytes = response.bodyAsBytes();
    }
    
    
    
    public void initLogin() throws IOException {
        String code = getSafeCode.getSafeCode(bytes);
        try {
            Map data = new HashMap();
            data.put("USERNAME", username);
            data.put("PASSWORD", password);
            data.put("RANDOMCODE", code);
            Connection connect = Jsoup.connect(url_Login)
                    .header("Accept",
                            "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*
public class GetSafeCode {

    private String url_safecode = "http://jwxt.gdufe.edu.cn/jsxsd/verifycode.servlet"; // 验证码


    public String getSafeCode(byte[] bytes) throws IOException {
        ImgIdenfy imgIdenfy = new ImgIdenfy();
        //InputStream inputStream = new URL(url_safecode).openStream();

        InputStream inputStream = new ByteArrayInputStream(bytes);
        BufferedImage sourceImg = ImageIO.read(inputStream);
        imgIdenfy.writeImage(sourceImg);
        int[][] imgArr = imgIdenfy.binaryImg(sourceImg); // 二值化
        imgIdenfy.removeByLine(imgArr); // 去除干扰先 引用传递
        int[][][] imgArrArr = imgIdenfy.imgCut(imgArr,
                new int[][]{new int[]{4, 13}, new int[]{14, 23}, new int[]{24, 33}, new int[]{34, 43}},
                new int[][]{new int[]{4, 16}, new int[]{4, 16}, new int[]{4, 16}, new int[]{4, 16}},
                4);
        return imgIdenfy.matchCode(imgArrArr);
    }
}
public class ImgIdenfy {

    private int height = 22;
    private int width = 62;
    private int rgbThres = 150;
    private String url_safecode = "http://jwxt.gdufe.edu.cn/jsxsd/verifycode.servlet"; // 验证码

    public int[][] binaryImg(BufferedImage img) {
        int[][] imgArr = new int[this.height][this.width];
        for (int x = 0; x < this.width; ++x) {
            for (int y = 0; y < this.height; ++y) {
                if (x == 0 || y == 0 || x == this.width - 1 || y == this.height - 1) {
                    imgArr[y][x] = 1;
                    continue;
                }
                int pixel = img.getRGB(x, y);
                if (((pixel & 0xff0000) >> 16) < this.rgbThres && ((pixel & 0xff00) >> 8) < this.rgbThres && (pixel & 0xff) < this.rgbThres) {
                    imgArr[y][x] = 0;
                } else {
                    imgArr[y][x] = 1;
                }
            }
        }
        return imgArr;
    }

    // 去掉干扰线
    public void removeByLin.e(int[][] imgArr) {
        for (int y = 1; y < this.height - 1; ++y) {
            for (int x = 1; x < this.width - 1; ++x) {
                if (imgArr[y][x] == 0) {
                    int count = imgArr[y][x - 1] + imgArr[y][x + 1] + imgArr[y + 1][x] + imgArr[y - 1][x];
                    if (count > 2) imgArr[y][x] = 1;
                }
            }
        }
    }

    // 裁剪
    public int[][][] imgCut(int[][] imgArr, int[][] xCut, int[][] yCut, int num) {
        int[][][] imgArrArr = new int[num][yCut[0][1] - yCut[0][0]][xCut[0][1] - xCut[0][0]];
        for (int i = 0; i < num; ++i) {
            for (int j = yCut[i][0]; j < yCut[i][1]; ++j) {
                for (int k = xCut[i][0]; k < xCut[i][1]; ++k) {
                    imgArrArr[i][j-yCut[i][0]][k-xCut[i][0]] = imgArr[j][k];
                }
            }
        }
        return imgArrArr;
    }

    // 转字符串
    public String getString(int[][] imgArr){
        StringBuilder s = new StringBuilder();
        int unitHeight = imgArr.length;
        int unitWidth = imgArr[0].length;
        for (int y = 0; y < unitHeight; ++y) {
            for (int x = 0; x < unitWidth; ++x) {
                s.append(imgArr[y][x]);
            }
        }
        return s.toString();
    }

    // 相同大小直接对比
    private int comparedText(String s1,String s2){
        int n = s1.length();
        int percent = 0;
        for(int i = 0; i < n ; ++i) {
            if (s1.charAt(i) == s2.charAt(i)) percent++;
        }
        return percent;
    }

    
    public String matchCode(int [][][] imgArrArr){
        StringBuilder s = new StringBuilder();
        Map charMap = CharMap.getCharMap();
        for (int[][] imgArr : imgArrArr){
            int maxMatch = 0;
            String tempRecord = "";
            for(Map.Entry m : charMap.entrySet()){
                int percent = this.comparedText(this.getString(imgArr),m.getValue());
                if(percent > maxMatch){
                    maxMatch = percent;
                    tempRecord = m.getKey();
                }
            }
            s.append(tempRecord);
        }
        return s.toString();
    }

    // 写入硬盘
    public void writeImage(BufferedImage sourceImg) {
        File imageFile = new File("v.jpg");
        try {
            FileOutputStream outStream = new FileOutputStream(imageFile);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ImageIO.write(sourceImg, "jpg", out);
            byte[] data = out.toByteArray();
            outStream.write(data);
        } catch (Exception e) {
            System.out.println(e.toString());
        }
    }

    // 控制台打印
    public void showImg(int[][] imgArr) {
        int unitHeight = imgArr.length;
        int unitWidth = imgArr[0].length;
        for (int y = 0; y < unitHeight; ++y) {
            for (int x = 0; x < unitWidth; ++x) {
                System.out.print(imgArr[y][x]);
            }
            System.out.println();
        }
    }
}

Jsoup解析网页

jsoup 是一个用于处理真实世界 HTML 的 Java 库。它使用最好的 HTML5 DOM 方法和 CSS 选择器提供了一个非常方便的 API,用于获取 URL 以及提取和操作数据。具体学习可以参考https://jsoup.org/

此项目中几乎每个网页解析都用到了jsoup,jsoup是比较简单的,这里描述一个页面解析的过程,其他的类似。


public class SelectiveCourseJsoup {
    

    public static List getHaveStudied(String studentId, String password) throws IOException {
        Map haveStudiedMap = new HashMap();
        Map data = new HashMap(); // 此map是用户请求体中的key-value对应,具体参数设置参考浏览器请求过程携带的参数

        // 模拟登录,获取cookie
        InitLogin initLogin = new InitLogin();
        initLogin.setUsername(studentId);
        initLogin.setPassword(password);
        initLogin.getcookie();
        initLogin.initLogin();

        double gpaUp = 0;
        double creditTotal = 0;


        //设置请求体参数
        data.put("kksj", "");
        data.put("kcxz", "");
        data.put("kcmc", "");
        data.put("fxkc", "0");
        data.put("xsfs", "all");
        
        // jsoup连接,携带map和cookie,使用post请求
        Connection connection = Jsoup.connect("http://jwxt.gdufe.edu.cn/jsxsd/kscj/cjcx_list")
                .header("Accept",
                        "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*
public class SelectiveCoursePoi {
    private static HashMap selectiveCourseMap;
    private static String filepath = "src\main\resources\static\List of elective courses.xlsx";
    public static HashMap getSelectiveCourse() {
        selectiveCourseMap = new HashMap();

        try {
            InputStream inputStream = new FileInputStream(filepath);
            XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
            Sheet sheet = workbook.getSheetAt(0);

            int maxRowNum = sheet.getPhysicalNumberOfRows();

            for(int i = 0; i < maxRowNum; i++) {
                Row row = sheet.getRow(i);

                String courseid = getCellStringValue(row.getCell(1));
                String coursename = getCellStringValue(row.getCell(2));
                Integer credit = Integer.parseInt(getCellStringValue(row.getCell(3)));
                String courseCollege = getCellStringValue(row.getCell(4));
                String coursetype = getCellStringValue(row.getCell(5));
                if(selectiveCourseMap.containsKey(coursename)) {
                    SelectiveCourse selectiveCourse = selectiveCourseMap.get(coursename);
                    List courseidList = selectiveCourse.getCourseid();
                    courseidList.add(courseid);
                    selectiveCourse.setCourseid(courseidList);

                    List creditList = selectiveCourse.getCredit();
                    creditList.add(credit);
                    selectiveCourse.setCredit(creditList);

                    selectiveCourseMap.put(coursename, selectiveCourse);
                } else {
                    List courseidList1 = new ArrayList();
                    List creditList1 = new ArrayList();
                    courseidList1.add(courseid);
                    creditList1.add(credit);

                    // SelectiveCourse是对应的实体类
                    SelectiveCourse selectiveCourse = new SelectiveCourse(coursetype, courseidList1, coursename, creditList1, courseCollege);
                    selectiveCourseMap.put(coursename, selectiveCourse);
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return selectiveCourseMap;
    }
   
    // 通用方法,处理excel单元格格式
    public static String getCellStringValue(Cell cell) {
        if (cell == null) {
            return "";
        }
        int type = cell.getCellType();
        String cellValue;
        switch (type) {
            case 3:
                cellValue = "";
                break;
            case 5  :
                cellValue = "";
                break;
            case 4:
                cellValue = String.valueOf(cell.getBooleanCellValue());
                break;
            case 0:
                cellValue = NumberToTextConverter.toText(cell.getNumericCellValue());
                break;
            case 1:
                cellValue = cell.getStringCellValue();
                break;
            case 2:
                cellValue = cell.getCellFormula();
                break;
            default:
                cellValue = "";
                break;
        }
        return cellValue;
    }
}

配置文件

跨域请求


@Configuration
public class                            CorsConfig implements WebMvcConfigurer {
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("

@Configuration //配置类
@EnableSwagger2// 开启Swagger2的自动配置
public class SwaggerConfig {

    @Bean //配置docket以配置Swagger具体参数
    public Docket docket() {

        return new Docket(documentationType.SWAGGER_2)
                .apiInfo(apiInfo())        // 将文档信息注入
                .groupName("waiterxiaoyy") // 分组名
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.waiterxiaoyy.backandroiddesign.controller")) // 扫描接口路径
                .build();
    }

    //配置文档信息
    private ApiInfo apiInfo() {
        Contact contact = new Contact("WaiterXiaoYY", "http://waiterxiaoyy.ltd/", "waiterxiaoyy@qq.com");
        return new ApiInfo(
                "接口文档", // 标题
                "[状态码:200-请求成功(返回数据) // 202-请求获取失败(捕获异常返回)]" , // 描述
                "v1.0", // 版本
                "", // 组织链接
                contact, // 联系人信息
                "", // 许可
                "", // 许可连接
                new ArrayList()// 扩展
        );
    }
}
接口样式 获取学生课程 接口URL

http://localhost:9093/api/getstucourse

请求方式

POST

Content-Type

json

请求Body参数
{
  "password": "xxx",
  "studentId": "1825xxxxxxxx",
  "termTime": "2021-2022-1"
}
参数名示例值参数类型参数描述
passwordxxxText教务系统密码
studentId1825xxxxxxxText学号
termTime2021-2022-1Text学期(格式:2021-2022-1)
成功响应示例
{
	"code": 200,
	"msg": "获取学生课程成功",
	"count": 1,
	"data": [
		{
			"studentid": "1825xxxxxxx",
			"coureseid": "2bf105e3-3",
			"course": "应用软件系统综合设计",
			"teacher": "胡建军副教授",
			"week": [
				"9-16(周)",
				"9-16(周)",
				"9-16(周)",
				"9-16(周)",
				"9-16(周)",
				"9-16(周)",
				"9-16(周)",
				"9-16(周)",
				"9-16(周)"
			],
			"position": [
				"实验楼803(软件工程第一实验室)",
				"实验楼803(软件工程第一实验室)",
				"实验楼803(软件工程第一实验室)",
				"实验楼803(软件工程第一实验室)",
				"实验楼803(软件工程第一实验室)",
				"实验楼803(软件工程第一实验室)",
				"实验楼803(软件工程第一实验室)",
				"实验楼803(软件工程第一实验室)",
				"实验楼803(软件工程第一实验室)"
			],
			"count": [
				"[01-02]节",
				"[03-04]节",
				"[05-06]节",
				"[01-02]节",
				"[03-04]节",
				"[05-06]节",
				"[01-02]节",
				"[03-04]节",
				"[05-06]节"
			],
			"weekCountPositon": [
				"9-16(周)[01-02]节实验楼803(软件工程第一实验室)",
				"9-16(周)[03-04]节实验楼803(软件工程第一实验室)",
				"9-16(周)[05-06]节实验楼803(软件工程第一实验室)"
			]
		}
	]
}
参数名示例值参数类型参数描述
code200Text
msg获取学生课程成功Text返回信息描述
count1Text
dataText返回数据
data.studentid1825xxxxxxxText学号
data.coureseid2bf105e3-3Text课程代号(后台随机生成,不是真正的课程号)
data.course应用软件系统综合设计Text课程名称
data.teacher胡建军副教授Text老师名称
data.week9-16(周)Text上课周期
data.position实验楼803(软件工程第一实验室)Text上课地点
data.count[01-02]节Text上课时间
data.weekCountPositon9-16(周)[01-02]节实验楼803(软件工程第一实验室)Text上课周期时间地点
失败响应示例
{
	"code": 202,
	"msg": "获取学生课程失败",
	"count": null,
	"data": null
}
参数名示例值参数类型参数描述
code202Text
msg获取学生课程失败Text返回文字描述
countText
dataText返回数据
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/711494.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号