新增物理实验成绩获取与解析

old-package
lensfrex 2 years ago
parent bffcca9455
commit 56c0f3c19e
Signed by: lensfrex
GPG Key ID: 0F69A0A2FBEE98A0
  1. 7
      mywust-core/pom.xml
  2. 4
      mywust-core/src/main/java/cn/linghang/mywust/core/api/PhysicsSystemUrls.java
  3. 39
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/physics/PhysicsScoreListPageParser.java
  4. 42
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/physics/PhysicsScorePageParser.java
  5. 2
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/UndergradCourseTableParser.java
  6. 82
      mywust-core/src/main/java/cn/linghang/mywust/core/request/physics/PhysicsSystemRequestFactory.java
  7. 4
      mywust-core/src/main/java/cn/linghang/mywust/core/request/undergrade/BkjxRequestFactory.java
  8. 7
      mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/PhysicsLogin.java
  9. 33
      mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/UndergraduateLogin.java
  10. 19
      mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsApiServiceBase.java
  11. 16
      mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsCourseApiServiceBase.java
  12. 52
      mywust-core/src/main/java/cn/linghang/mywust/core/service/physics/PhysicsScoreApiServiceBase.java
  13. 15
      mywust-core/src/main/java/cn/linghang/mywust/core/util/JsoupUtil.java
  14. 15
      mywust-core/src/main/java/cn/linghang/mywust/core/util/PageFormExtractor.java
  15. 6
      mywust-test/pom.xml
  16. 8
      mywust-util/pom.xml
  17. 9
      mywust-util/src/main/java/cn/linghang/mywust/util/StringUtil.java

@ -54,6 +54,12 @@
<version>2.14.0-rc1</version> <version>2.14.0-rc1</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>${hutool.version}</version>
</dependency>
</dependencies> </dependencies>
<properties> <properties>
@ -63,5 +69,6 @@
<slf4j.version>2.0.3</slf4j.version> <slf4j.version>2.0.3</slf4j.version>
<jsoup.version>1.15.3</jsoup.version> <jsoup.version>1.15.3</jsoup.version>
<hutool.version>5.8.12</hutool.version>
</properties> </properties>
</project> </project>

@ -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_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_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"; public static final String PHYSICS_COURSE_API = "http://wlsy.wust.edu.cn:7101/Page/PEE/PEECM/PEECM0001.aspx?action=PEE110301&&parentId=PEE11";
} }

@ -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<PhysicsScoreListPageParser.PhysicsScoreListPageParseResult> {
private static final String scorePageLinkXpath = "//*[@id=\"ID_PEE63_gvpeeCourseScore\"]/tbody/tr/td/a";
@Override
public PhysicsScoreListPageParseResult parse(String html) throws ParseException {
List<String> 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<String> courseIds;
private final String term;
}
}

@ -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<List<Score>> {
private static final String scoreRowXpath = "//*[@id=\"ID_PEE63_gvpeeLabScore\"]/tbody/tr[@style=\"background-color:White;height:25px;\"]";
@Override
public List<Score> parse(String html) throws ParseException {
Elements scoreRows = Jsoup.parse(html).selectXpath(scoreRowXpath);
List<Score> 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;
}
}

@ -86,7 +86,7 @@ public class UndergradCourseTableParser implements Parser<List<Course>> {
courseBuilder.endSection(lineIndex * 2 + 2); courseBuilder.endSection(lineIndex * 2 + 2);
// 不直接使用String.split而是手动分割,是因为系统自带split方法每次调用都需要编译一次切割正则,效率不太行 // 不直接使用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<String> times = StringUtil.split(timeText, ','); List<String> times = StringUtil.split(timeText, ',');
for (String time : times) { for (String time : times) {
Matcher weekMatcher = WEEK_REGEX.matcher(time); Matcher weekMatcher = WEEK_REGEX.matcher(time);

@ -2,6 +2,7 @@ package cn.linghang.mywust.core.request.physics;
import cn.linghang.mywust.core.api.PhysicsSystemUrls; import cn.linghang.mywust.core.api.PhysicsSystemUrls;
import cn.linghang.mywust.core.request.RequestFactory; 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.network.entitys.HttpRequest;
import cn.linghang.mywust.util.StringUtil; import cn.linghang.mywust.util.StringUtil;
@ -14,9 +15,13 @@ public class PhysicsSystemRequestFactory extends RequestFactory {
return makeHttpRequest(PhysicsSystemUrls.PHYSICS_LOGIN_INDEX); return makeHttpRequest(PhysicsSystemUrls.PHYSICS_LOGIN_INDEX);
} }
public static HttpRequest loginCookiesRequest(String username, String password, String cookies) { public static HttpRequest loginCookiesRequest(String username, String password, String loginIndexHtml) {
byte[] queryData = StringUtil.generateQueryString(makeLoginQueryParam(username, password)).getBytes(StandardCharsets.UTF_8); Map<String, String> params = PageFormExtractor.extractAllParams(loginIndexHtml);
return makeHttpRequest(PhysicsSystemUrls.PHYSICS_LOGIN_COOKIES_API, queryData, cookies); 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) { public static HttpRequest mainIndexRequest(String cookies) {
@ -27,33 +32,52 @@ public class PhysicsSystemRequestFactory extends RequestFactory {
return makeHttpRequest(redirect, null, cookies); return makeHttpRequest(redirect, null, cookies);
} }
public static HttpRequest physicsCourseIndexRequest(Map<String, String> 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) { public static HttpRequest physicsCourseRequest(String cookies) {
return makeHttpRequest(PhysicsSystemUrls.PHYSICS_COURSE_API, null, cookies); return makeHttpRequest(PhysicsSystemUrls.PHYSICS_COURSE_API, null, cookies);
} }
private static Map<String, String> makeLoginQueryParam(String username, String password) { public static HttpRequest physicsScoreListRequest(String cookies) {
// 这几个算是是写死了的,也能用 return makeHttpRequest(PhysicsSystemUrls.PHYSICS_SCORE_LIST_URL, null, cookies);
// 但其实最好还是从首页动态获取某些关键字段("__VIEWSTATE") }
// 这种办法等后面有时间了再实现
public static HttpRequest physicsScoreRequest(String cookies, String courseId, Map<String, String> params) {
String data = StringUtil.generateQueryString(makeScoreQueryParam(courseId, params));
return makeStringDataHttpRequest(PhysicsSystemUrls.PHYSICS_SCORE_URL, data, cookies);
}
private static Map<String, String> makeScoreQueryParam(String courseId, Map<String, String> params) {
Map<String, String> 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<String, String> makeLoginQueryParam(String username, String password, String viewState) {
Map<String, String> queryParams = new HashMap<>(12); Map<String, String> queryParams = new HashMap<>(12);
queryParams.put("__EVENTTARGET", ""); queryParams.put("__EVENTTARGET", "");
queryParams.put("__EVENTARGUMENT", ""); queryParams.put("__EVENTARGUMENT", "");
queryParams.put("__VIEWSTATE", VIEW_STATE); queryParams.put("__VIEWSTATE", viewState);
queryParams.put("__VIEWSTATEGENERATOR", "F42971E6"); queryParams.put("__VIEWSTATEGENERATOR", "F42971E6");
queryParams.put("hidUserID", ""); queryParams.put("hidUserID", "");
queryParams.put("hidUserPassWord", ""); queryParams.put("hidUserPassWord", "");
@ -66,18 +90,4 @@ public class PhysicsSystemRequestFactory extends RequestFactory {
return queryParams; 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";
} }

@ -108,7 +108,7 @@ public class BkjxRequestFactory extends RequestFactory {
return makeHttpRequest(UndergradUrls.Legacy.BKJX_DATA_STRING_API); return makeHttpRequest(UndergradUrls.Legacy.BKJX_DATA_STRING_API);
} }
public static HttpRequest ticketRedirectRequest(String encode) { public static HttpRequest ticketRedirectRequest(String encode, String cookies) {
Map<String, String> queryParams = new HashMap<>(4); Map<String, String> queryParams = new HashMap<>(4);
queryParams.put("userAccount", ""); queryParams.put("userAccount", "");
queryParams.put("userPassword", ""); queryParams.put("userPassword", "");
@ -120,7 +120,7 @@ public class BkjxRequestFactory extends RequestFactory {
extendHeaders.put("Referer", "http://bkjx.wust.edu.cn/"); extendHeaders.put("Referer", "http://bkjx.wust.edu.cn/");
extendHeaders.put("Origin", "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); .addHeaders(extendHeaders);
} }
} }

@ -26,8 +26,13 @@ public class PhysicsLogin {
} }
public String getLoginCookie(String username, String password, RequestClientOption requestClientOption) throws IOException, ApiException, ParseException { 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其实在这步就能获取到,不需要再请求一遍首页获取 // 直接登录,ASP.NET_SessionId其实在这步就能获取到,不需要再请求一遍首页获取
HttpRequest loginCookieRequest = PhysicsSystemRequestFactory.loginCookiesRequest(username, password, null); HttpRequest loginCookieRequest = PhysicsSystemRequestFactory.loginCookiesRequest(username, password, loginIndex);
HttpResponse loginCookieResponse = requester.post(loginCookieRequest, requestClientOption); HttpResponse loginCookieResponse = requester.post(loginCookieRequest, requestClientOption);
if (loginCookieResponse.getStatusCode() != HttpResponse.HTTP_REDIRECT_302) { if (loginCookieResponse.getStatusCode() != HttpResponse.HTTP_REDIRECT_302) {
throw new ApiException(ApiException.Code.PHYSICS_PASSWORD_WRONG); throw new ApiException(ApiException.Code.PHYSICS_PASSWORD_WRONG);

@ -35,16 +35,7 @@ public class UndergraduateLogin {
HttpResponse sessionResponse = requester.get(sessionRequest, requestOption); HttpResponse sessionResponse = requester.get(sessionRequest, requestOption);
String cookies = sessionResponse.getCookies(); String cookies = sessionResponse.getCookies();
if (roughCheckCookieFail(cookies)) { this.checkCookie(cookies, requestOption);
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);
}
return cookies; return cookies;
} }
@ -56,26 +47,24 @@ public class UndergraduateLogin {
* *
* @return 获取到的Cookies * @return 获取到的Cookies
*/ */
@Deprecated
public String getLoginCookieLegacy(String username, String password, RequestClientOption requestOption) throws IOException, ApiException { public String getLoginCookieLegacy(String username, String password, RequestClientOption requestOption) throws IOException, ApiException {
// 获取某段神秘的dataStr(反正官网代码是这么叫的) // 获取某段神秘的dataStr(反正官网代码是这么叫的)
HttpRequest dataStringRequest = BkjxRequestFactory.Legacy.dataStringRequest(); HttpRequest dataStringRequest = BkjxRequestFactory.Legacy.dataStringRequest();
HttpResponse dataStringResponse = requester.post(dataStringRequest, requestOption); HttpResponse dataStringResponse = requester.post(dataStringRequest, requestOption);
if (dataStringResponse.getBody() == null) { if (dataStringResponse.getBody() == null) {
log.warn("[mywust]: 本科教学系统旧版登录方式:获取dataStr时发生错误"); log.warn("[mywust]: 本科教学系统旧版登录:获取dataStr时发生错误");
throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION); throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION);
} }
String dataString = new String(dataStringResponse.getBody()); String dataString = dataStringResponse.getStringBody();
// 获取登录ticket // 获取登录ticket
String encoded = PasswordEncoder.legacyPassword(username, password, dataString); String encoded = PasswordEncoder.legacyPassword(username, password, dataString);
HttpRequest ticketRequest = BkjxRequestFactory.Legacy.ticketRedirectRequest(encoded); HttpRequest ticketRequest = BkjxRequestFactory.Legacy.ticketRedirectRequest(encoded, dataStringResponse.getCookies());
ticketRequest.setCookies(dataStringResponse.getCookies());
HttpResponse ticketResponse = requester.post(ticketRequest, requestOption); HttpResponse ticketResponse = requester.post(ticketRequest, requestOption);
if (ticketResponse.getBody() == null) { if (ticketResponse.getBody() == null) {
log.warn("[mywust]: 本科教学系统旧版登录方式:获取登录ticket时发生错误"); log.warn("[mywust]: 本科教学系统旧版登录:获取登录ticket时发生错误");
throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION); throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION);
} }
@ -89,18 +78,22 @@ public class UndergraduateLogin {
HttpResponse sessionResponse = requester.get(sessionRequest, requestOption); HttpResponse sessionResponse = requester.get(sessionRequest, requestOption);
String cookies = sessionResponse.getCookies(); String cookies = sessionResponse.getCookies();
this.checkCookie(cookies, requestOption);
return cookies;
}
private void checkCookie(String cookies, RequestClientOption requestOption) throws ApiException, IOException {
if (roughCheckCookieFail(cookies)) { if (roughCheckCookieFail(cookies)) {
log.error("[mywust]: Cookie粗查不通过:{}", cookies); log.error("[mywust]: Cookie粗查不通过:{}", cookies);
throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, "登录获取的Cookie无效"); throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, "登录获取的Cookie无效");
} }
// 检查Cookie是否真正可用,同时请求一次任意接口使后续接口能够正确响应 // 检查Cookie是否真正可用,同时请求一次任意接口使后续接口能够正确响应
// 拿到Cookie后调用的第一个接口会产生302/301跳转,需要再次调用才能正确响应 // 拿到Cookie后调用的第一个接口有时候会产生302/301跳转到主页,需要再次调用才能正确响应
if (checkCookiesFail(cookies, requestOption)) { if (checkCookiesFail(cookies, requestOption)) {
log.warn("[mywust]: Cookie检查不通过:{}", cookies); log.warn("[mywust]: Cookie检查不通过:{}", cookies);
} }
return cookies;
} }
private boolean roughCheckCookieFail(String cookies) { private boolean roughCheckCookieFail(String cookies) {

@ -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);
}
}
}

@ -9,22 +9,18 @@ import cn.linghang.mywust.network.entitys.HttpResponse;
import java.io.IOException; import java.io.IOException;
public class PhysicsApiService { public class PhysicsCourseApiServiceBase extends PhysicsApiServiceBase {
private final Requester requester; public PhysicsCourseApiServiceBase(Requester requester) {
super(requester);
public PhysicsApiService(Requester requester) {
this.requester = requester;
} }
public String getCoursePage(String cookie, RequestClientOption requestClientOption) throws IOException, ApiException { public String getPage(String cookie, RequestClientOption requestClientOption) throws IOException, ApiException {
requestClientOption.setFallowUrlRedirect(false); requestClientOption.setFallowUrlRedirect(false);
// 请求真正的课表页 // 直接请求真正的课表页
HttpRequest coursePageRequest = PhysicsSystemRequestFactory.physicsCourseRequest(cookie); HttpRequest coursePageRequest = PhysicsSystemRequestFactory.physicsCourseRequest(cookie);
HttpResponse courseResponse = requester.get(coursePageRequest, requestClientOption); HttpResponse courseResponse = requester.get(coursePageRequest, requestClientOption);
if (courseResponse.getStatusCode() != HttpResponse.HTTP_OK) { checkResponse(courseResponse);
throw new ApiException(ApiException.Code.COOKIE_INVALID);
}
return new String(courseResponse.getBody()); return new String(courseResponse.getBody());
} }

@ -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<String, String> 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<String> 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<String, String> pageParams = PageFormExtractor.extractAllParams(scoreListPage);
pageParams.put("ID_PEE63$ddlxq", pageParseResult.getTerm());
List<String> courseIds = pageParseResult.getCourseIds();
List<String> scorePages = new ArrayList<>(courseIds.size());
for (String courseId : courseIds) {
scorePages.add(this.getPage(cookie, courseId, pageParams, requestClientOption));
}
return scorePages;
}
}

@ -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时返回空字符串 * 取元素集合中第一个元素的文本当elements为null或数量为0时返回空字符串
* *

@ -5,7 +5,10 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* html页面表单参数提取用于简化部分繁琐的表单数据生成模拟网页表单数据的请求参数 * html页面表单参数提取用于简化部分繁琐的表单数据生成模拟网页表单数据的请求参数
@ -24,4 +27,16 @@ public class PageFormExtractor {
targetMap.put(element.attr("name"), element.attr("value")); targetMap.put(element.attr("name"), element.attr("value"));
} }
} }
private static final Pattern formPattern = Pattern.compile("name=\"(?<name>.*?)\"(.*?)value=\"(?<value>.*?)\"");
public static Map<String, String> extractAllParams(String wholeHtml) {
Map<String, String> params = new HashMap<>(16);
Matcher matcher = formPattern.matcher(wholeHtml);
while (matcher.find()) {
params.put(matcher.group("name"), matcher.group("value"));
}
return params;
}
} }

@ -61,6 +61,12 @@
<version>RELEASE</version> <version>RELEASE</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>cn.linghang</groupId>
<artifactId>mywust-core</artifactId>
<version>0.0.2-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>

@ -15,6 +15,8 @@
<maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target> <maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<hutool.version>5.8.12</hutool.version>
</properties> </properties>
<dependencies> <dependencies>
@ -30,5 +32,11 @@
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
<version>31.1-jre</version> <version>31.1-jre</version>
</dependency> </dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>${hutool.version}</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

@ -1,10 +1,10 @@
package cn.linghang.mywust.util; package cn.linghang.mywust.util;
import cn.hutool.core.util.URLUtil;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
@ -20,12 +20,7 @@ public class StringUtil {
public static String generateQueryString(Map<String, String> queryParams) { public static String generateQueryString(Map<String, String> queryParams) {
// 自动对value值进行url编码 // 自动对value值进行url编码
Map<String, String> urlEncodedQueryParams = new TreeMap<>(REPEATABLE_COMPARATOR); Map<String, String> urlEncodedQueryParams = new TreeMap<>(REPEATABLE_COMPARATOR);
queryParams.forEach((k, v) -> { queryParams.forEach((k, v) -> urlEncodedQueryParams.put(URLUtil.encodeAll(k), URLUtil.encodeAll(v)));
try {
urlEncodedQueryParams.put(k, URLEncoder.encode(v, StandardCharsets.UTF_8.name()));
} catch (UnsupportedEncodingException ignored) {
}
});
return Joiner.on('&') return Joiner.on('&')
.useForNull("") .useForNull("")

Loading…
Cancel
Save