parent
b78577fc28
commit
efab489d17
@ -1,4 +1,14 @@ |
|||||||
package cn.linghang.mywust.core.exception; |
package cn.linghang.mywust.core.exception; |
||||||
|
|
||||||
public class BasicException extends Exception { |
public class BasicException extends Exception { |
||||||
|
public BasicException() { |
||||||
|
} |
||||||
|
|
||||||
|
public BasicException(String message) { |
||||||
|
super(message); |
||||||
|
} |
||||||
|
|
||||||
|
public BasicException(String message, Throwable cause) { |
||||||
|
super(message, cause); |
||||||
|
} |
||||||
} |
} |
||||||
|
@ -1,4 +1,14 @@ |
|||||||
package cn.linghang.mywust.core.exception; |
package cn.linghang.mywust.core.exception; |
||||||
|
|
||||||
public class CookieInvalidException extends BasicException { |
public class CookieInvalidException extends BasicException { |
||||||
|
public CookieInvalidException() { |
||||||
|
} |
||||||
|
|
||||||
|
public CookieInvalidException(String message) { |
||||||
|
super(message); |
||||||
|
} |
||||||
|
|
||||||
|
public CookieInvalidException(String message, Throwable cause) { |
||||||
|
super(message, cause); |
||||||
|
} |
||||||
} |
} |
||||||
|
@ -1,4 +1,15 @@ |
|||||||
package cn.linghang.mywust.core.exception; |
package cn.linghang.mywust.core.exception; |
||||||
|
|
||||||
public class ParseException extends BasicException { |
public class ParseException extends BasicException { |
||||||
|
public ParseException() { |
||||||
|
super("解析数据失败"); |
||||||
|
} |
||||||
|
|
||||||
|
public ParseException(String message) { |
||||||
|
super(message); |
||||||
|
} |
||||||
|
|
||||||
|
public ParseException(String message, Throwable cause) { |
||||||
|
super(message, cause); |
||||||
|
} |
||||||
} |
} |
||||||
|
@ -1,4 +1,14 @@ |
|||||||
package cn.linghang.mywust.core.exception; |
package cn.linghang.mywust.core.exception; |
||||||
|
|
||||||
public class PasswordWornException extends BasicException { |
public class PasswordWornException extends BasicException { |
||||||
|
public PasswordWornException() { |
||||||
|
} |
||||||
|
|
||||||
|
public PasswordWornException(String message) { |
||||||
|
super(message); |
||||||
|
} |
||||||
|
|
||||||
|
public PasswordWornException(String message, Throwable cause) { |
||||||
|
super(message, cause); |
||||||
|
} |
||||||
} |
} |
||||||
|
@ -0,0 +1,57 @@ |
|||||||
|
package cn.linghang.mywust.core.parser.undergraduate; |
||||||
|
|
||||||
|
import cn.linghang.mywust.core.exception.ParseException; |
||||||
|
import cn.linghang.mywust.core.parser.Parser; |
||||||
|
import cn.linghang.mywust.core.parser.undergraduate.xpath.ExamInfoXpath; |
||||||
|
import cn.linghang.mywust.model.undergrade.ExamInfo; |
||||||
|
import org.jsoup.Jsoup; |
||||||
|
import org.jsoup.select.Elements; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public class ExamInfoParser implements Parser<List<ExamInfo>> { |
||||||
|
private static final Logger log = LoggerFactory.getLogger(ExamInfoParser.class); |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<ExamInfo> parse(String html) throws ParseException { |
||||||
|
Elements rows = Jsoup.parse(html).selectXpath(ExamInfoXpath.EXAM_INFO_ROWS_XPATH); |
||||||
|
if (rows.isEmpty()) { |
||||||
|
throw new ParseException(); |
||||||
|
} |
||||||
|
|
||||||
|
List<ExamInfo> examInfos = new ArrayList<>(rows.size()); |
||||||
|
|
||||||
|
try { |
||||||
|
for (int i = 1; i < rows.size(); i++) { |
||||||
|
Elements columns = rows.get(i).getElementsByTag("td"); |
||||||
|
|
||||||
|
ExamInfo examInfo = new ExamInfo(); |
||||||
|
|
||||||
|
// 这段看着震撼,但其实很丑
|
||||||
|
examInfo.setId(columns.get(0).text()); |
||||||
|
examInfo.setTerm(columns.get(1).text()); |
||||||
|
examInfo.setCourseNumber(columns.get(2).text()); |
||||||
|
examInfo.setCourseName(columns.get(3).text()); |
||||||
|
examInfo.setGroupName(columns.get(4).text()); |
||||||
|
examInfo.setScore(columns.get(5).text()); |
||||||
|
examInfo.setFlag(columns.get(6).text()); |
||||||
|
examInfo.setCredit(columns.get(7).text()); |
||||||
|
examInfo.setCourseHours(columns.get(8).text()); |
||||||
|
examInfo.setGradePoint(columns.get(9).text()); |
||||||
|
examInfo.setEvaluateMethod(columns.get(11).text()); |
||||||
|
examInfo.setKind(columns.get(12).text()); |
||||||
|
examInfo.setCourseKind(columns.get(13).text()); |
||||||
|
|
||||||
|
examInfos.add(examInfo); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
log.warn("解析成绩页面时发生错误:{}", e.getMessage()); |
||||||
|
log.warn("终止解析,返回已解析数据"); |
||||||
|
} |
||||||
|
|
||||||
|
return examInfos; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package cn.linghang.mywust.core.parser.undergraduate; |
||||||
|
|
||||||
|
import cn.linghang.mywust.core.exception.ParseException; |
||||||
|
import cn.linghang.mywust.core.parser.Parser; |
||||||
|
import org.jsoup.Jsoup; |
||||||
|
import org.jsoup.nodes.Element; |
||||||
|
|
||||||
|
public class SchemePageParser implements Parser<String> { |
||||||
|
|
||||||
|
@Override |
||||||
|
public String parse(String html) throws ParseException { |
||||||
|
Element schemeElement = Jsoup.parse(html).getElementById("dataList"); |
||||||
|
if (schemeElement == null) { |
||||||
|
throw new ParseException("教学方案html解析提取失败,id为dataList的元素不存在"); |
||||||
|
} |
||||||
|
|
||||||
|
return schemeElement.outerHtml(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
package cn.linghang.mywust.core.parser.undergraduate.xpath; |
||||||
|
|
||||||
|
public class ExamInfoXpath { |
||||||
|
public static final String EXAM_INFO_ROWS_XPATH = "//*[@id=\"dataList\"]/tbody/tr"; |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
package cn.linghang.mywust.core.service.undergraduate; |
||||||
|
|
||||||
|
import cn.linghang.mywust.core.exception.CookieInvalidException; |
||||||
|
import cn.linghang.mywust.core.exception.ParseException; |
||||||
|
import cn.linghang.mywust.core.parser.undergraduate.ExamInfoParser; |
||||||
|
import cn.linghang.mywust.core.request.BkjxRequestFactory; |
||||||
|
import cn.linghang.mywust.core.util.BkjxUtil; |
||||||
|
import cn.linghang.mywust.model.undergrade.ExamInfo; |
||||||
|
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 org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public class ExamInfoApi { |
||||||
|
private static final Logger log = LoggerFactory.getLogger(ExamInfoApi.class); |
||||||
|
|
||||||
|
private final Requester requester; |
||||||
|
|
||||||
|
private static final ExamInfoParser parser = new ExamInfoParser(); |
||||||
|
|
||||||
|
public ExamInfoApi(Requester requester) { |
||||||
|
this.requester = requester; |
||||||
|
} |
||||||
|
|
||||||
|
public String getExamInfoPage(String cookies, RequestClientOption requestClientOption) throws IOException, CookieInvalidException { |
||||||
|
HttpRequest request = BkjxRequestFactory.examScoreInfoRequest(cookies, "", "", ""); |
||||||
|
HttpResponse response = requester.post(request, requestClientOption); |
||||||
|
|
||||||
|
// 检查响应是否正确
|
||||||
|
if (response.getBody() == null || |
||||||
|
response.getStatusCode() != HttpResponse.HTTP_OK || |
||||||
|
BkjxUtil.checkLoginFinger(response.getBody())) { |
||||||
|
|
||||||
|
throw new CookieInvalidException("[请求获取成绩]:Cookie无效:" + cookies); |
||||||
|
} |
||||||
|
|
||||||
|
return new String(response.getBody()); |
||||||
|
} |
||||||
|
|
||||||
|
public List<ExamInfo> getExamInfo(String cookies, RequestClientOption requestClientOption) throws IOException, CookieInvalidException, ParseException { |
||||||
|
String examInfoPage = this.getExamInfoPage(cookies, requestClientOption); |
||||||
|
|
||||||
|
return parser.parse(examInfoPage); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
package cn.linghang.mywust.core.service.undergraduate; |
||||||
|
|
||||||
|
import cn.linghang.mywust.core.exception.CookieInvalidException; |
||||||
|
import cn.linghang.mywust.core.exception.ParseException; |
||||||
|
import cn.linghang.mywust.core.parser.undergraduate.SchemePageParser; |
||||||
|
import cn.linghang.mywust.core.request.BkjxRequestFactory; |
||||||
|
import cn.linghang.mywust.core.util.BkjxUtil; |
||||||
|
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; |
||||||
|
|
||||||
|
public class SchemeApi { |
||||||
|
private final Requester requester; |
||||||
|
|
||||||
|
private static final SchemePageParser parser = new SchemePageParser(); |
||||||
|
|
||||||
|
public SchemeApi(Requester requester) { |
||||||
|
this.requester = requester; |
||||||
|
} |
||||||
|
|
||||||
|
public String getSchemePage(String cookies, RequestClientOption requestClientOption) throws CookieInvalidException, IOException { |
||||||
|
HttpRequest request = BkjxRequestFactory.schemePageRequest(cookies); |
||||||
|
HttpResponse response = requester.get(request, requestClientOption); |
||||||
|
|
||||||
|
// 检查响应是否正确
|
||||||
|
if (response.getBody() == null || |
||||||
|
response.getStatusCode() != HttpResponse.HTTP_OK || |
||||||
|
BkjxUtil.checkLoginFinger(response.getBody())) { |
||||||
|
|
||||||
|
throw new CookieInvalidException("[请求获取成绩]:Cookie无效:" + cookies); |
||||||
|
} |
||||||
|
|
||||||
|
return new String(response.getBody()); |
||||||
|
} |
||||||
|
|
||||||
|
public String getPrueSchemePage(String cookies, RequestClientOption requestClientOption) throws IOException, CookieInvalidException, ParseException { |
||||||
|
String fullPage = this.getSchemePage(cookies, requestClientOption); |
||||||
|
return parser.parse(fullPage); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,88 @@ |
|||||||
|
package cn.linghang.mywust.model.undergrade; |
||||||
|
|
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Builder; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.NoArgsConstructor; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p>考试成绩信息实体类</p> |
||||||
|
* <br> |
||||||
|
* <p>一个对象实体对应着一次考试,也就是系统里边看到的一行成绩</p> |
||||||
|
* <p>决定全都用string存储,交给上层调用者自行处理转换</p> |
||||||
|
* <p>看似浪费内存,但实际上解析出来的就是string,string转int/float然后又转成其他的什么后其实是更耗内存的</p> |
||||||
|
* <p>既然如此不如直接存解析出来的值,具体如何使用就交给上层自行决定</p> |
||||||
|
* |
||||||
|
* @author lensfrex |
||||||
|
* @created 2022-10-26 14:29 |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
@Builder |
||||||
|
@NoArgsConstructor |
||||||
|
@AllArgsConstructor |
||||||
|
public class ExamInfo { |
||||||
|
/** |
||||||
|
* 序号id,在某些场景下可能会有用 |
||||||
|
*/ |
||||||
|
private String id; |
||||||
|
|
||||||
|
/** |
||||||
|
* 学期 |
||||||
|
*/ |
||||||
|
private String term; |
||||||
|
|
||||||
|
/** |
||||||
|
* 课程编号 |
||||||
|
*/ |
||||||
|
private String courseNumber; |
||||||
|
|
||||||
|
/** |
||||||
|
* 课程名称 |
||||||
|
*/ |
||||||
|
private String courseName; |
||||||
|
|
||||||
|
/** |
||||||
|
* 分组名 |
||||||
|
*/ |
||||||
|
private String groupName; |
||||||
|
|
||||||
|
/** |
||||||
|
* 成绩 |
||||||
|
*/ |
||||||
|
private String score; |
||||||
|
|
||||||
|
/** |
||||||
|
* 成绩标识(缺考) |
||||||
|
*/ |
||||||
|
private String flag; |
||||||
|
|
||||||
|
/** |
||||||
|
* 学分 |
||||||
|
*/ |
||||||
|
private String credit; |
||||||
|
|
||||||
|
/** |
||||||
|
* 学时 |
||||||
|
*/ |
||||||
|
private String courseHours; |
||||||
|
|
||||||
|
/** |
||||||
|
* 绩点 |
||||||
|
*/ |
||||||
|
private String gradePoint; |
||||||
|
|
||||||
|
/** |
||||||
|
* 考核方式 |
||||||
|
*/ |
||||||
|
private String evaluateMethod; |
||||||
|
|
||||||
|
/** |
||||||
|
* 考试性质 |
||||||
|
*/ |
||||||
|
private String kind; |
||||||
|
|
||||||
|
/** |
||||||
|
* 课程性质 |
||||||
|
*/ |
||||||
|
private String courseKind; |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
package cn.linghang.mywust.network.entitys; |
||||||
|
|
||||||
|
import cn.linghang.mywust.util.StringUtil; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.TreeMap; |
||||||
|
|
||||||
|
public class FormBodyBuilder { |
||||||
|
private final Map<String, String> queryParams; |
||||||
|
|
||||||
|
public FormBodyBuilder(Map<String, String> queryParams) { |
||||||
|
this.queryParams = queryParams; |
||||||
|
} |
||||||
|
|
||||||
|
public FormBodyBuilder() { |
||||||
|
this.queryParams = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); |
||||||
|
} |
||||||
|
|
||||||
|
public FormBodyBuilder(int initSize) { |
||||||
|
this.queryParams = new HashMap<>(initSize); |
||||||
|
} |
||||||
|
|
||||||
|
public FormBodyBuilder addQueryParams(Map<String, String> params) { |
||||||
|
this.queryParams.putAll(params); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public FormBodyBuilder addQueryParam(String key, String value) { |
||||||
|
this.queryParams.put(key, value); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Map<String, String> build() { |
||||||
|
return this.queryParams; |
||||||
|
} |
||||||
|
|
||||||
|
public String buildAndToString() { |
||||||
|
return StringUtil.generateQueryString(this.queryParams); |
||||||
|
} |
||||||
|
} |
@ -1,4 +1,4 @@ |
|||||||
package cn.linghang.mywust.network; |
package cn.linghang.mywust.network.entitys; |
||||||
|
|
||||||
import lombok.Data; |
import lombok.Data; |
||||||
|
|
@ -1,4 +1,4 @@ |
|||||||
package cn.linghang.mywust.network; |
package cn.linghang.mywust.network.entitys; |
||||||
|
|
||||||
import lombok.Data; |
import lombok.Data; |
||||||
|
|
@ -0,0 +1,45 @@ |
|||||||
|
import cn.linghang.mywust.core.exception.BasicException; |
||||||
|
import cn.linghang.mywust.core.service.undergraduate.ExamInfoApi; |
||||||
|
import cn.linghang.mywust.model.undergrade.ExamInfo; |
||||||
|
import cn.linghang.mywust.network.RequestClientOption; |
||||||
|
import cn.linghang.mywust.network.Requester; |
||||||
|
import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Scanner; |
||||||
|
|
||||||
|
public class ExamInfoTest { |
||||||
|
public static void main(String[] args) throws BasicException, IOException { |
||||||
|
new ExamInfoTest().run(); |
||||||
|
} |
||||||
|
|
||||||
|
private void run() throws BasicException, IOException { |
||||||
|
System.out.println("成绩获取"); |
||||||
|
System.out.println("Cookie:"); |
||||||
|
|
||||||
|
Scanner scanner = new Scanner(System.in); |
||||||
|
|
||||||
|
String cookie = scanner.nextLine(); |
||||||
|
|
||||||
|
System.out.println("使用Cookie:" + cookie); |
||||||
|
|
||||||
|
Requester requester = new SimpleOkhttpRequester(); |
||||||
|
ExamInfoApi jwcService = new ExamInfoApi(requester); |
||||||
|
|
||||||
|
RequestClientOption option = new RequestClientOption(); |
||||||
|
option.setTimeout(5); |
||||||
|
RequestClientOption.Proxy proxy = new RequestClientOption.Proxy(); |
||||||
|
proxy.setPort(6060); |
||||||
|
proxy.setAddress("127.0.0.1"); |
||||||
|
option.setProxy(proxy); |
||||||
|
option.setFallowUrlRedirect(false); |
||||||
|
|
||||||
|
List<ExamInfo> infos = jwcService.getExamInfo(cookie, option); |
||||||
|
|
||||||
|
for (ExamInfo info : infos) { |
||||||
|
|
||||||
|
System.out.println(info); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
import cn.linghang.mywust.core.exception.BasicException; |
||||||
|
import cn.linghang.mywust.core.service.undergraduate.ExamInfoApi; |
||||||
|
import cn.linghang.mywust.core.service.undergraduate.SchemeApi; |
||||||
|
import cn.linghang.mywust.model.undergrade.ExamInfo; |
||||||
|
import cn.linghang.mywust.network.RequestClientOption; |
||||||
|
import cn.linghang.mywust.network.Requester; |
||||||
|
import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Scanner; |
||||||
|
|
||||||
|
public class SchemeTest { |
||||||
|
public static void main(String[] args) throws BasicException, IOException { |
||||||
|
new SchemeTest().run(); |
||||||
|
} |
||||||
|
|
||||||
|
private void run() throws BasicException, IOException { |
||||||
|
System.out.println("培养方案获取"); |
||||||
|
System.out.println("Cookie:"); |
||||||
|
|
||||||
|
Scanner scanner = new Scanner(System.in); |
||||||
|
|
||||||
|
String cookie = scanner.nextLine(); |
||||||
|
|
||||||
|
System.out.println("使用Cookie:" + cookie); |
||||||
|
|
||||||
|
RequestClientOption option = new RequestClientOption(); |
||||||
|
option.setTimeout(5); |
||||||
|
RequestClientOption.Proxy proxy = new RequestClientOption.Proxy(); |
||||||
|
proxy.setPort(6060); |
||||||
|
proxy.setAddress("127.0.0.1"); |
||||||
|
option.setProxy(proxy); |
||||||
|
option.setFallowUrlRedirect(false); |
||||||
|
|
||||||
|
Requester requester = new SimpleOkhttpRequester(); |
||||||
|
SchemeApi jwcService = new SchemeApi(requester); |
||||||
|
|
||||||
|
String page = jwcService.getPrueSchemePage(cookie, option); |
||||||
|
|
||||||
|
System.out.println(page); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
import cn.linghang.mywust.core.exception.BasicException; |
||||||
|
import cn.linghang.mywust.core.parser.undergraduate.StudentInfoPageParser; |
||||||
|
import cn.linghang.mywust.core.service.auth.JwcLogin; |
||||||
|
import cn.linghang.mywust.core.service.auth.UnionLogin; |
||||||
|
import cn.linghang.mywust.core.service.undergraduate.StudentInfoApi; |
||||||
|
import cn.linghang.mywust.model.undergrade.StudentInfo; |
||||||
|
import cn.linghang.mywust.network.RequestClientOption; |
||||||
|
import cn.linghang.mywust.network.Requester; |
||||||
|
import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Scanner; |
||||||
|
|
||||||
|
public class StudentInfoPageTest { |
||||||
|
public static void main(String[] args) throws BasicException, IOException { |
||||||
|
new StudentInfoPageTest().run(); |
||||||
|
} |
||||||
|
|
||||||
|
private void run() throws BasicException, IOException { |
||||||
|
System.out.println("学生信息获取"); |
||||||
|
System.out.println("Cookie:"); |
||||||
|
|
||||||
|
Scanner scanner = new Scanner(System.in); |
||||||
|
|
||||||
|
String cookie = scanner.nextLine(); |
||||||
|
|
||||||
|
System.out.println("使用Cookie:" + cookie); |
||||||
|
|
||||||
|
Requester requester = new SimpleOkhttpRequester(); |
||||||
|
StudentInfoApi jwcService = new StudentInfoApi(requester); |
||||||
|
|
||||||
|
RequestClientOption option = new RequestClientOption(); |
||||||
|
option.setTimeout(5); |
||||||
|
option.setProxy(null); |
||||||
|
option.setFallowUrlRedirect(false); |
||||||
|
|
||||||
|
StudentInfo info = jwcService.getStudentInfo(cookie, option); |
||||||
|
|
||||||
|
System.out.println(info); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue