From 56c0f3c19ea46d75565bb9feaf5e4b27dbdc0c5a Mon Sep 17 00:00:00 2001 From: lensferno Date: Sat, 18 Feb 2023 11:39:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=89=A9=E7=90=86=E5=AE=9E?= =?UTF-8?q?=E9=AA=8C=E6=88=90=E7=BB=A9=E8=8E=B7=E5=8F=96=E4=B8=8E=E8=A7=A3?= =?UTF-8?q?=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mywust-core/pom.xml | 7 ++ .../mywust/core/api/PhysicsSystemUrls.java | 4 + .../physics/PhysicsScoreListPageParser.java | 39 +++++++++ .../physics/PhysicsScorePageParser.java | 42 ++++++++++ .../UndergradCourseTableParser.java | 2 +- .../physics/PhysicsSystemRequestFactory.java | 82 +++++++++++-------- .../undergrade/BkjxRequestFactory.java | 4 +- .../core/service/auth/PhysicsLogin.java | 7 +- .../core/service/auth/UndergraduateLogin.java | 33 +++----- .../physics/PhysicsApiServiceBase.java | 19 +++++ ....java => PhysicsCourseApiServiceBase.java} | 16 ++-- .../physics/PhysicsScoreApiServiceBase.java | 52 ++++++++++++ .../linghang/mywust/core/util/JsoupUtil.java | 15 ++++ .../mywust/core/util/PageFormExtractor.java | 15 ++++ mywust-test/pom.xml | 6 ++ mywust-util/pom.xml | 8 ++ .../cn/linghang/mywust/util/StringUtil.java | 9 +- 17 files changed, 283 insertions(+), 77 deletions(-) create mode 100644 mywust-core/src/main/java/cn/linghang/mywust/core/parser/physics/PhysicsScoreListPageParser.java create mode 100644 mywust-core/src/main/java/cn/linghang/mywust/core/parser/physics/PhysicsScorePageParser.java create mode 100644 mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsApiServiceBase.java rename mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/{PhysicsApiService.java => PhysicsCourseApiServiceBase.java} (61%) create mode 100644 mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsScoreApiServiceBase.java diff --git a/mywust-core/pom.xml b/mywust-core/pom.xml index 0b4c3c3..bae1a93 100644 --- a/mywust-core/pom.xml +++ b/mywust-core/pom.xml @@ -54,6 +54,12 @@ 2.14.0-rc1 compile + + + cn.hutool + hutool-core + ${hutool.version} + @@ -63,5 +69,6 @@ 2.0.3 1.15.3 + 5.8.12 \ No newline at end of file diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/api/PhysicsSystemUrls.java b/mywust-core/src/main/java/cn/linghang/mywust/core/api/PhysicsSystemUrls.java index 4be73c3..8041fb5 100644 --- a/mywust-core/src/main/java/cn/linghang/mywust/core/api/PhysicsSystemUrls.java +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/api/PhysicsSystemUrls.java @@ -9,6 +9,10 @@ public class PhysicsSystemUrls { public static final String PHYSICS_SYSTEM_INDEX_URL = "http://wlsy.wust.edu.cn:7101/Page/PEE/PEECM/PEECM0001.aspx"; + public static final String PHYSICS_SCORE_LIST_URL = "http://wlsy.wust.edu.cn:7101/Page/PEE/PEECM/PEECM0001.aspx?flag=2&&action=PEE63&&moduleId=PEE63"; + + public static final String PHYSICS_SCORE_URL = "http://wlsy.wust.edu.cn:7101/Page/PEE/PEECM/PEECM0001.aspx?flag=2&&&action=PEE63&moduleId=PEE63"; + public static final String PHYSICS_COURSE_INDEX_URL = "http://wlsy.wust.edu.cn:7101/Page/PEE/PEECM/PEECM0001.aspx?flag=1&&action=PEE110101&&moduleId=PEE11"; public static final String PHYSICS_COURSE_API = "http://wlsy.wust.edu.cn:7101/Page/PEE/PEECM/PEECM0001.aspx?action=PEE110301&&parentId=PEE11"; } diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/parser/physics/PhysicsScoreListPageParser.java b/mywust-core/src/main/java/cn/linghang/mywust/core/parser/physics/PhysicsScoreListPageParser.java new file mode 100644 index 0000000..304f5b3 --- /dev/null +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/parser/physics/PhysicsScoreListPageParser.java @@ -0,0 +1,39 @@ +package cn.linghang.mywust.core.parser.physics; + +import cn.linghang.mywust.core.exception.ParseException; +import cn.linghang.mywust.core.parser.Parser; +import cn.linghang.mywust.core.util.JsoupUtil; +import lombok.Data; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.util.ArrayList; +import java.util.List; + +public class PhysicsScoreListPageParser implements Parser { + private static final String scorePageLinkXpath = "//*[@id=\"ID_PEE63_gvpeeCourseScore\"]/tbody/tr/td/a"; + + @Override + public PhysicsScoreListPageParseResult parse(String html) throws ParseException { + List scoreIds = new ArrayList<>(2); + Document document = Jsoup.parse(html); + Elements links = document.selectXpath(scorePageLinkXpath); + for (Element link : links) { + String href = link.attr("href"); + scoreIds.add(href.replaceAll("javascript:__doPostBack\\('(.*?)',''\\)", "$1")); + } + + Element termSelect = document.getElementById("ID_PEE63_ddlxq"); + String term = JsoupUtil.getSelectValue(termSelect); + + return new PhysicsScoreListPageParseResult(scoreIds, term); + } + + @Data + public static class PhysicsScoreListPageParseResult { + private final List courseIds; + private final String term; + } +} diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/parser/physics/PhysicsScorePageParser.java b/mywust-core/src/main/java/cn/linghang/mywust/core/parser/physics/PhysicsScorePageParser.java new file mode 100644 index 0000000..d41ac50 --- /dev/null +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/parser/physics/PhysicsScorePageParser.java @@ -0,0 +1,42 @@ +package cn.linghang.mywust.core.parser.physics; + +import cn.linghang.mywust.core.exception.ParseException; +import cn.linghang.mywust.core.parser.Parser; +import cn.linghang.mywust.core.util.JsoupUtil; +import cn.linghang.mywust.data.global.Score; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.util.ArrayList; +import java.util.List; + +public class PhysicsScorePageParser implements Parser> { + private static final String scoreRowXpath = "//*[@id=\"ID_PEE63_gvpeeLabScore\"]/tbody/tr[@style=\"background-color:White;height:25px;\"]"; + + @Override + public List parse(String html) throws ParseException { + Elements scoreRows = Jsoup.parse(html).selectXpath(scoreRowXpath); + + List scores = new ArrayList<>(scoreRows.size()); + for (Element scoreRow : scoreRows) { + Elements girds = scoreRow.getElementsByTag("td"); + // 某行格子数少于8个,即到了成绩那部分就没了,就跳过 + if (girds.size() < 8) { + continue; + } + + // 有用的信息就这么多 + Score score = new Score(); + score.setId(JsoupUtil.getElementText(girds, 0)); + score.setCourseName(JsoupUtil.getElementText(girds, 1)); + score.setGroupName(JsoupUtil.getElementText(girds, 2)); + score.setFlag(JsoupUtil.getElementText(girds, 6)); + score.setScore(JsoupUtil.getElementText(girds, 7)); + + scores.add(score); + } + + return scores; + } +} diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/UndergradCourseTableParser.java b/mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/UndergradCourseTableParser.java index af87383..767d384 100644 --- a/mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/UndergradCourseTableParser.java +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/UndergradCourseTableParser.java @@ -86,7 +86,7 @@ public class UndergradCourseTableParser implements Parser> { courseBuilder.endSection(lineIndex * 2 + 2); // 不直接使用String.split而是手动分割,是因为系统自带split方法每次调用都需要编译一次切割正则,效率不太行 - String timeText = timeElements.isEmpty() ? "" : StringUtil.split(timeElements.get(0).text(), ',').get(0); + String timeText = timeElements.isEmpty() ? "" : StringUtil.split(JsoupUtil.getElementText(timeElements, 0), ',').get(0); List times = StringUtil.split(timeText, ','); for (String time : times) { Matcher weekMatcher = WEEK_REGEX.matcher(time); diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/request/physics/PhysicsSystemRequestFactory.java b/mywust-core/src/main/java/cn/linghang/mywust/core/request/physics/PhysicsSystemRequestFactory.java index 8e1b31e..2f6aecd 100644 --- a/mywust-core/src/main/java/cn/linghang/mywust/core/request/physics/PhysicsSystemRequestFactory.java +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/request/physics/PhysicsSystemRequestFactory.java @@ -2,6 +2,7 @@ package cn.linghang.mywust.core.request.physics; import cn.linghang.mywust.core.api.PhysicsSystemUrls; import cn.linghang.mywust.core.request.RequestFactory; +import cn.linghang.mywust.core.util.PageFormExtractor; import cn.linghang.mywust.network.entitys.HttpRequest; import cn.linghang.mywust.util.StringUtil; @@ -14,9 +15,13 @@ public class PhysicsSystemRequestFactory extends RequestFactory { return makeHttpRequest(PhysicsSystemUrls.PHYSICS_LOGIN_INDEX); } - public static HttpRequest loginCookiesRequest(String username, String password, String cookies) { - byte[] queryData = StringUtil.generateQueryString(makeLoginQueryParam(username, password)).getBytes(StandardCharsets.UTF_8); - return makeHttpRequest(PhysicsSystemUrls.PHYSICS_LOGIN_COOKIES_API, queryData, cookies); + public static HttpRequest loginCookiesRequest(String username, String password, String loginIndexHtml) { + Map params = PageFormExtractor.extractAllParams(loginIndexHtml); + String viewState = params.get("__VIEWSTATE"); + + String queryData = StringUtil.generateQueryString(makeLoginQueryParam(username, password, viewState)); + + return makeHttpRequest(PhysicsSystemUrls.PHYSICS_LOGIN_COOKIES_API, queryData); } public static HttpRequest mainIndexRequest(String cookies) { @@ -27,33 +32,52 @@ public class PhysicsSystemRequestFactory extends RequestFactory { return makeHttpRequest(redirect, null, cookies); } - public static HttpRequest physicsCourseIndexRequest(Map indexParam, String cookies) { - // 补上“动态”生成的参数 - indexParam.put("smLabManage", "upnMenu|lnk_2"); - indexParam.put("__EVENTTARGET", "lnk_2"); - indexParam.put("ID_PEE01$NoticeType", "-999"); - indexParam.put("ID_PEE01$txtCreater", ""); - indexParam.put("ID_PEE01$IsRead", "-999"); - indexParam.put("ID_PEE01$ObjPager_input", "1"); - indexParam.put("__ASYNCPOST", "true"); - - byte[] fromData = StringUtil.generateQueryString(indexParam).getBytes(StandardCharsets.UTF_8); - return makeHttpRequest(PhysicsSystemUrls.PHYSICS_COURSE_INDEX_URL, fromData, cookies); - } - public static HttpRequest physicsCourseRequest(String cookies) { return makeHttpRequest(PhysicsSystemUrls.PHYSICS_COURSE_API, null, cookies); } - private static Map makeLoginQueryParam(String username, String password) { - // 这几个算是是写死了的,也能用 - // 但其实最好还是从首页动态获取某些关键字段("__VIEWSTATE") - // 这种办法等后面有时间了再实现 + public static HttpRequest physicsScoreListRequest(String cookies) { + return makeHttpRequest(PhysicsSystemUrls.PHYSICS_SCORE_LIST_URL, null, cookies); + } + + public static HttpRequest physicsScoreRequest(String cookies, String courseId, Map params) { + String data = StringUtil.generateQueryString(makeScoreQueryParam(courseId, params)); + return makeStringDataHttpRequest(PhysicsSystemUrls.PHYSICS_SCORE_URL, data, cookies); + } + + private static Map makeScoreQueryParam(String courseId, Map params) { + Map queryParams = new HashMap<>(17); + + String viewState = params.get("__VIEWSTATE"); + String student = params.get("ID_PEE63$hidStudentID"); + // 可能是写死的,但不好说 + String server = params.get("ID_PEE63$hidPG"); + String term = params.get("ID_PEE63$ddlxq"); + + queryParams.put("smLabManage", "ID_PEE63$upn140201|" + courseId); + queryParams.put("__EVENTTARGET", courseId); + queryParams.put("__EVENTARGUMENT", ""); + queryParams.put("__LASTFOCUS", ""); + queryParams.put("__VIEWSTATE", viewState); + queryParams.put("__VIEWSTATEGENERATOR", "4B5F03FE"); + queryParams.put("hidRoleType", ""); + queryParams.put("ID_PEE63$hidStudentID", student); + queryParams.put("ID_PEE63$hidCourseID", ""); + queryParams.put("ID_PEE63$hidColumnNum", ""); + queryParams.put("ID_PEE63$hidPG", server); + // 这个需要手动补充 + queryParams.put("ID_PEE63$ddlxq", term); + queryParams.put("__ASYNCPOST", "true"); + + return queryParams; + } + + private static Map makeLoginQueryParam(String username, String password, String viewState) { Map queryParams = new HashMap<>(12); queryParams.put("__EVENTTARGET", ""); queryParams.put("__EVENTARGUMENT", ""); - queryParams.put("__VIEWSTATE", VIEW_STATE); + queryParams.put("__VIEWSTATE", viewState); queryParams.put("__VIEWSTATEGENERATOR", "F42971E6"); queryParams.put("hidUserID", ""); queryParams.put("hidUserPassWord", ""); @@ -66,18 +90,4 @@ public class PhysicsSystemRequestFactory extends RequestFactory { return queryParams; } - - private static final String VIEW_STATE = - "/wEPDwULLTEwNTgzMzY4NTgPZBYCZg9kFgICCQ9kFgJmD2QWBgINDw9kFgIeBVN0" + - "eWxlBZsBbGluZS1oZWlnaHQ6NThweDtwYWRkaW5nLWxlZnQ6NDVweDtwYWRkaW5n" + - "LXRvcDowcHg7d2lkdGg6MzE1cHg7aGVpZ2h0OjU0cHg7YmFja2dyb3VuZDp1cmwo" + - "Li4vLi4vUmVzb3VyY2UvQmFzaWNJbmZvL25ld0JHL+eUqOaIt+WQjS5wbmcpIG5v" + - "LXJlcGVhdCAwcHggMHB4OztkAg8PD2QWAh8ABZsBbGluZS1oZWlnaHQ6NThweDtw" + - "YWRkaW5nLWxlZnQ6NDVweDtwYWRkaW5nLXRvcDowcHg7d2lkdGg6MzE1cHg7aGVp" + - "Z2h0OjU0cHg7YmFja2dyb3VuZDp1cmwoLi4vLi4vUmVzb3VyY2UvQmFzaWNJbmZv" + - "L25ld0JHL+Wvhueggeepui5wbmcpIG5vLXJlcGVhdCAwcHggMHB4OztkAhkPDxYC" + - "HgRUZXh0BRvns7vnu5/orr/pl67kurrmrKHvvJozNjc2MDJkZBgBBR5fX0NvbnRy" + - "b2xzUmVxdWlyZVBvc3RCYWNrS2V5X18WBQULaW1nQnRuQ2xvc2UFCGxidGJTYXZl" + - "BQpsYnRuQ2FuY2VsBQhidG5Mb2dpbgUKY2JTYXZlSW5mb6K8UayJuWe2OSRVqLDo" + - "2J4wMKAT"; } diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/request/undergrade/BkjxRequestFactory.java b/mywust-core/src/main/java/cn/linghang/mywust/core/request/undergrade/BkjxRequestFactory.java index 7cb3c3d..fc7ddaf 100644 --- a/mywust-core/src/main/java/cn/linghang/mywust/core/request/undergrade/BkjxRequestFactory.java +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/request/undergrade/BkjxRequestFactory.java @@ -108,7 +108,7 @@ public class BkjxRequestFactory extends RequestFactory { return makeHttpRequest(UndergradUrls.Legacy.BKJX_DATA_STRING_API); } - public static HttpRequest ticketRedirectRequest(String encode) { + public static HttpRequest ticketRedirectRequest(String encode, String cookies) { Map queryParams = new HashMap<>(4); queryParams.put("userAccount", ""); queryParams.put("userPassword", ""); @@ -120,7 +120,7 @@ public class BkjxRequestFactory extends RequestFactory { extendHeaders.put("Referer", "http://bkjx.wust.edu.cn/"); extendHeaders.put("Origin", "http://bkjx.wust.edu.cn"); - return makeHttpRequest(UndergradUrls.Legacy.BKJX_SESSION_COOKIE_API, queryString.getBytes(StandardCharsets.UTF_8)) + return makeHttpRequest(UndergradUrls.Legacy.BKJX_SESSION_COOKIE_API, queryString.getBytes(StandardCharsets.UTF_8), cookies) .addHeaders(extendHeaders); } } diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/PhysicsLogin.java b/mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/PhysicsLogin.java index 4360cf6..2af12fe 100644 --- a/mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/PhysicsLogin.java +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/PhysicsLogin.java @@ -26,8 +26,13 @@ public class PhysicsLogin { } public String getLoginCookie(String username, String password, RequestClientOption requestClientOption) throws IOException, ApiException, ParseException { + // 获取“动态”的表单参数,例如__VIEWSTATE等 + HttpRequest loginIndexPageRequest = PhysicsSystemRequestFactory.loginIndexRequest(); + HttpResponse loginIndexPageResponse = requester.get(loginIndexPageRequest); + String loginIndex = loginIndexPageResponse.getStringBody(); + // 直接登录,ASP.NET_SessionId其实在这步就能获取到,不需要再请求一遍首页获取 - HttpRequest loginCookieRequest = PhysicsSystemRequestFactory.loginCookiesRequest(username, password, null); + HttpRequest loginCookieRequest = PhysicsSystemRequestFactory.loginCookiesRequest(username, password, loginIndex); HttpResponse loginCookieResponse = requester.post(loginCookieRequest, requestClientOption); if (loginCookieResponse.getStatusCode() != HttpResponse.HTTP_REDIRECT_302) { throw new ApiException(ApiException.Code.PHYSICS_PASSWORD_WRONG); diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/UndergraduateLogin.java b/mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/UndergraduateLogin.java index 5fd23d3..295eaa2 100644 --- a/mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/UndergraduateLogin.java +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/UndergraduateLogin.java @@ -35,16 +35,7 @@ public class UndergraduateLogin { HttpResponse sessionResponse = requester.get(sessionRequest, requestOption); String cookies = sessionResponse.getCookies(); - if (roughCheckCookieFail(cookies)) { - log.error("[mywust]: Cookie粗查不通过:{}", cookies); - throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, "登录获取的Cookie无效"); - } - - // 检查Cookie是否真正可用,同时请求一次任意接口使后续接口能够正确响应 - // 拿到Cookie后调用的第一个接口会产生302/301跳转,需要再次调用才能正确响应 - if (checkCookiesFail(cookies, requestOption)) { - log.warn("[mywust]: Cookie检查不通过:{}", cookies); - } + this.checkCookie(cookies, requestOption); return cookies; } @@ -56,26 +47,24 @@ public class UndergraduateLogin { * * @return 获取到的Cookies */ - @Deprecated public String getLoginCookieLegacy(String username, String password, RequestClientOption requestOption) throws IOException, ApiException { // 获取某段神秘的dataStr(反正官网代码是这么叫的) HttpRequest dataStringRequest = BkjxRequestFactory.Legacy.dataStringRequest(); HttpResponse dataStringResponse = requester.post(dataStringRequest, requestOption); if (dataStringResponse.getBody() == null) { - log.warn("[mywust]: 本科教学系统旧版登录方式:获取dataStr时发生错误"); + log.warn("[mywust]: 本科教学系统旧版登录:获取dataStr时发生错误"); throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION); } - String dataString = new String(dataStringResponse.getBody()); + String dataString = dataStringResponse.getStringBody(); // 获取登录ticket String encoded = PasswordEncoder.legacyPassword(username, password, dataString); - HttpRequest ticketRequest = BkjxRequestFactory.Legacy.ticketRedirectRequest(encoded); - ticketRequest.setCookies(dataStringResponse.getCookies()); - + HttpRequest ticketRequest = BkjxRequestFactory.Legacy.ticketRedirectRequest(encoded, dataStringResponse.getCookies()); HttpResponse ticketResponse = requester.post(ticketRequest, requestOption); + if (ticketResponse.getBody() == null) { - log.warn("[mywust]: 本科教学系统旧版登录方式:获取登录ticket时发生错误"); + log.warn("[mywust]: 本科教学系统旧版登录:获取登录ticket时发生错误"); throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION); } @@ -89,18 +78,22 @@ public class UndergraduateLogin { HttpResponse sessionResponse = requester.get(sessionRequest, requestOption); String cookies = sessionResponse.getCookies(); + this.checkCookie(cookies, requestOption); + + return cookies; + } + + private void checkCookie(String cookies, RequestClientOption requestOption) throws ApiException, IOException { if (roughCheckCookieFail(cookies)) { log.error("[mywust]: Cookie粗查不通过:{}", cookies); throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, "登录获取的Cookie无效"); } // 检查Cookie是否真正可用,同时请求一次任意接口使后续接口能够正确响应 - // 拿到Cookie后调用的第一个接口会产生302/301跳转,需要再次调用才能正确响应 + // 拿到Cookie后调用的第一个接口有时候会产生302/301跳转到主页,需要再次调用才能正确响应 if (checkCookiesFail(cookies, requestOption)) { log.warn("[mywust]: Cookie检查不通过:{}", cookies); } - - return cookies; } private boolean roughCheckCookieFail(String cookies) { diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsApiServiceBase.java b/mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsApiServiceBase.java new file mode 100644 index 0000000..fb104dd --- /dev/null +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsApiServiceBase.java @@ -0,0 +1,19 @@ +package cn.linghang.mywust.core.service.physics; + +import cn.linghang.mywust.core.exception.ApiException; +import cn.linghang.mywust.network.Requester; +import cn.linghang.mywust.network.entitys.HttpResponse; + +public abstract class PhysicsApiServiceBase { + protected final Requester requester; + + public PhysicsApiServiceBase(Requester requester) { + this.requester = requester; + } + + protected void checkResponse(HttpResponse response) throws ApiException { + if (response.getStatusCode() != HttpResponse.HTTP_OK) { + throw new ApiException(ApiException.Code.COOKIE_INVALID); + } + } +} diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsApiService.java b/mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsCourseApiServiceBase.java similarity index 61% rename from mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsApiService.java rename to mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsCourseApiServiceBase.java index d99ca4e..599a5ca 100644 --- a/mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsApiService.java +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsCourseApiServiceBase.java @@ -9,22 +9,18 @@ import cn.linghang.mywust.network.entitys.HttpResponse; import java.io.IOException; -public class PhysicsApiService { - private final Requester requester; - - public PhysicsApiService(Requester requester) { - this.requester = requester; +public class PhysicsCourseApiServiceBase extends PhysicsApiServiceBase { + public PhysicsCourseApiServiceBase(Requester requester) { + super(requester); } - public String getCoursePage(String cookie, RequestClientOption requestClientOption) throws IOException, ApiException { + public String getPage(String cookie, RequestClientOption requestClientOption) throws IOException, ApiException { requestClientOption.setFallowUrlRedirect(false); - // 请求真正的课表页 + // 直接请求真正的课表页 HttpRequest coursePageRequest = PhysicsSystemRequestFactory.physicsCourseRequest(cookie); HttpResponse courseResponse = requester.get(coursePageRequest, requestClientOption); - if (courseResponse.getStatusCode() != HttpResponse.HTTP_OK) { - throw new ApiException(ApiException.Code.COOKIE_INVALID); - } + checkResponse(courseResponse); return new String(courseResponse.getBody()); } diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsScoreApiServiceBase.java b/mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsScoreApiServiceBase.java new file mode 100644 index 0000000..70abbfe --- /dev/null +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsScoreApiServiceBase.java @@ -0,0 +1,52 @@ +package cn.linghang.mywust.core.service.physics; + +import cn.linghang.mywust.core.exception.ApiException; +import cn.linghang.mywust.core.exception.ParseException; +import cn.linghang.mywust.core.parser.physics.PhysicsScoreListPageParser; +import cn.linghang.mywust.core.request.physics.PhysicsSystemRequestFactory; +import cn.linghang.mywust.core.util.PageFormExtractor; +import cn.linghang.mywust.network.RequestClientOption; +import cn.linghang.mywust.network.Requester; +import cn.linghang.mywust.network.entitys.HttpRequest; +import cn.linghang.mywust.network.entitys.HttpResponse; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class PhysicsScoreApiServiceBase extends PhysicsApiServiceBase { + + private static final PhysicsScoreListPageParser scoreListPageParser = new PhysicsScoreListPageParser(); + + public PhysicsScoreApiServiceBase(Requester requester) { + super(requester); + } + + public String getPage(String cookie, String courseId, Map pageParams, RequestClientOption requestClientOption) throws IOException, ApiException, ParseException { + HttpRequest scorePageRequest = PhysicsSystemRequestFactory.physicsScoreRequest(cookie, courseId, pageParams); + HttpResponse scorePageResponse = requester.post(scorePageRequest, requestClientOption); + checkResponse(scorePageResponse); + + return scorePageResponse.getStringBody(); + } + + public List getAllPages(String cookie, RequestClientOption requestClientOption) throws IOException, ParseException, ApiException { + HttpRequest scoreListPageRequest = PhysicsSystemRequestFactory.physicsScoreListRequest(cookie); + HttpResponse scoreListPageResponse = requester.get(scoreListPageRequest, requestClientOption); + String scoreListPage = scoreListPageResponse.getStringBody(); + + PhysicsScoreListPageParser.PhysicsScoreListPageParseResult pageParseResult = scoreListPageParser.parse(scoreListPage); + + Map pageParams = PageFormExtractor.extractAllParams(scoreListPage); + pageParams.put("ID_PEE63$ddlxq", pageParseResult.getTerm()); + + List courseIds = pageParseResult.getCourseIds(); + List scorePages = new ArrayList<>(courseIds.size()); + for (String courseId : courseIds) { + scorePages.add(this.getPage(cookie, courseId, pageParams, requestClientOption)); + } + + return scorePages; + } +} diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/util/JsoupUtil.java b/mywust-core/src/main/java/cn/linghang/mywust/core/util/JsoupUtil.java index cbcd0e3..bc386bb 100644 --- a/mywust-core/src/main/java/cn/linghang/mywust/core/util/JsoupUtil.java +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/util/JsoupUtil.java @@ -92,6 +92,21 @@ public class JsoupUtil { } } + /** + * 从select类型的Element中拿取到已选中的选项值value字段 + * + * @param element 元素对象 + * @return 相应的值,若element为空则返回空字符串 + */ + public static String getSelectValue(Element element) { + if (element == null) { + return ""; + } else { + return element.getElementsByAttributeValue("selected", "selected") + .attr("value"); + } + } + /** * 取元素集合中第一个元素的文本,当elements为null或数量为0时,返回空字符串 * diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/util/PageFormExtractor.java b/mywust-core/src/main/java/cn/linghang/mywust/core/util/PageFormExtractor.java index 8bbb048..fcea906 100644 --- a/mywust-core/src/main/java/cn/linghang/mywust/core/util/PageFormExtractor.java +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/util/PageFormExtractor.java @@ -5,7 +5,10 @@ import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; +import java.util.HashMap; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * html页面表单参数提取,用于简化部分繁琐的表单数据生成,模拟网页表单数据的请求参数 @@ -24,4 +27,16 @@ public class PageFormExtractor { targetMap.put(element.attr("name"), element.attr("value")); } } + + private static final Pattern formPattern = Pattern.compile("name=\"(?.*?)\"(.*?)value=\"(?.*?)\""); + + public static Map extractAllParams(String wholeHtml) { + Map params = new HashMap<>(16); + Matcher matcher = formPattern.matcher(wholeHtml); + while (matcher.find()) { + params.put(matcher.group("name"), matcher.group("value")); + } + + return params; + } } diff --git a/mywust-test/pom.xml b/mywust-test/pom.xml index 35887c8..63a376f 100644 --- a/mywust-test/pom.xml +++ b/mywust-test/pom.xml @@ -61,6 +61,12 @@ RELEASE test + + cn.linghang + mywust-core + 0.0.2-SNAPSHOT + compile + diff --git a/mywust-util/pom.xml b/mywust-util/pom.xml index 42ae0fc..b146440 100644 --- a/mywust-util/pom.xml +++ b/mywust-util/pom.xml @@ -15,6 +15,8 @@ 1.8 8 UTF-8 + + 5.8.12 @@ -30,5 +32,11 @@ guava 31.1-jre + + + cn.hutool + hutool-core + ${hutool.version} + \ No newline at end of file diff --git a/mywust-util/src/main/java/cn/linghang/mywust/util/StringUtil.java b/mywust-util/src/main/java/cn/linghang/mywust/util/StringUtil.java index 0a96a1c..59bc893 100644 --- a/mywust-util/src/main/java/cn/linghang/mywust/util/StringUtil.java +++ b/mywust-util/src/main/java/cn/linghang/mywust/util/StringUtil.java @@ -1,10 +1,10 @@ package cn.linghang.mywust.util; +import cn.hutool.core.util.URLUtil; import com.google.common.base.Joiner; import org.apache.commons.codec.binary.Base64; import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.*; @@ -20,12 +20,7 @@ public class StringUtil { public static String generateQueryString(Map queryParams) { // 自动对value值进行url编码 Map urlEncodedQueryParams = new TreeMap<>(REPEATABLE_COMPARATOR); - queryParams.forEach((k, v) -> { - try { - urlEncodedQueryParams.put(k, URLEncoder.encode(v, StandardCharsets.UTF_8.name())); - } catch (UnsupportedEncodingException ignored) { - } - }); + queryParams.forEach((k, v) -> urlEncodedQueryParams.put(URLUtil.encodeAll(k), URLUtil.encodeAll(v))); return Joiner.on('&') .useForNull("")