parent
b9e75fcd51
commit
f352b34b6f
@ -0,0 +1,4 @@ |
|||||||
|
package cn.linghang.mywust.core.exception; |
||||||
|
|
||||||
|
public class CookieInvalidException extends BasicException { |
||||||
|
} |
@ -0,0 +1,4 @@ |
|||||||
|
package cn.linghang.mywust.core.exception; |
||||||
|
|
||||||
|
public class HtmlPageParseException extends BasicException { |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
package cn.linghang.mywust.core.parser; |
||||||
|
|
||||||
|
import cn.linghang.mywust.core.exception.HtmlPageParseException; |
||||||
|
|
||||||
|
public interface Parser<T> { |
||||||
|
public T parse(String html) throws HtmlPageParseException; |
||||||
|
} |
@ -0,0 +1,64 @@ |
|||||||
|
package cn.linghang.mywust.core.parser.undergraduate; |
||||||
|
|
||||||
|
import cn.linghang.mywust.core.exception.HtmlPageParseException; |
||||||
|
import cn.linghang.mywust.core.parser.Parser; |
||||||
|
import cn.linghang.mywust.core.parser.undergraduate.xpath.StudentInfoXpath; |
||||||
|
import cn.linghang.mywust.model.undergrade.StudentInfo; |
||||||
|
import org.jsoup.Jsoup; |
||||||
|
import org.jsoup.nodes.Document; |
||||||
|
import org.jsoup.nodes.Element; |
||||||
|
import org.jsoup.select.Elements; |
||||||
|
|
||||||
|
public class StudentInfoPageParser implements Parser<StudentInfo> { |
||||||
|
|
||||||
|
public StudentInfo parse(String html) throws HtmlPageParseException { |
||||||
|
Document page = Jsoup.parse(html); |
||||||
|
Element table = page.getElementById("xjkpTable"); |
||||||
|
if (table == null) { |
||||||
|
throw new HtmlPageParseException(); |
||||||
|
} |
||||||
|
|
||||||
|
Elements studentElements = table.selectXpath(StudentInfoXpath.STUDENT_NUMBER); |
||||||
|
String studentNumber = studentElements.isEmpty() ? null : studentElements.get(0).text().replace("学号:", ""); |
||||||
|
|
||||||
|
Elements collegeElements = table.selectXpath(StudentInfoXpath.COLLEGE); |
||||||
|
String college = collegeElements.isEmpty() ? null : collegeElements.get(0).text().replace("院系:", ""); |
||||||
|
|
||||||
|
Elements majorElements = table.selectXpath(StudentInfoXpath.MAJOR); |
||||||
|
String major = majorElements.isEmpty() ? null : majorElements.get(0).text().replace("专业:", ""); |
||||||
|
|
||||||
|
Elements classElements = table.selectXpath(StudentInfoXpath.CLASS); |
||||||
|
String clazz = classElements.isEmpty() ? null : classElements.get(0).text().replace("班级:", ""); |
||||||
|
|
||||||
|
Elements nameElements = table.selectXpath(StudentInfoXpath.NAME); |
||||||
|
String name = nameElements.isEmpty() ? null : nameElements.get(0).text(); |
||||||
|
|
||||||
|
Elements sexElements = table.selectXpath(StudentInfoXpath.SEX); |
||||||
|
String sex = sexElements.isEmpty() ? null : sexElements.get(0).text(); |
||||||
|
|
||||||
|
Elements birthdayElements = table.selectXpath(StudentInfoXpath.BIRTHDAY); |
||||||
|
String birthday = birthdayElements.isEmpty() ? null : birthdayElements.get(0).text(); |
||||||
|
|
||||||
|
Elements hometownElements = table.selectXpath(StudentInfoXpath.HOMETOWN); |
||||||
|
String hometown = hometownElements.isEmpty() ? null : hometownElements.get(0).text(); |
||||||
|
|
||||||
|
Elements nationalityElements = table.selectXpath(StudentInfoXpath.NATIONALITY); |
||||||
|
String nationality = nationalityElements.isEmpty() ? null : nationalityElements.get(0).text(); |
||||||
|
|
||||||
|
Elements idNumberElements = table.selectXpath(StudentInfoXpath.ID_NUMBER); |
||||||
|
String idNumber = idNumberElements.isEmpty() ? null : idNumberElements.get(0).text(); |
||||||
|
|
||||||
|
return StudentInfo.builder() |
||||||
|
.studentNumber(studentNumber) |
||||||
|
.college(college) |
||||||
|
.major(major) |
||||||
|
.clazz(clazz) |
||||||
|
.name(name) |
||||||
|
.sex(sex) |
||||||
|
.birthday(birthday) |
||||||
|
.hometown(hometown) |
||||||
|
.nationality(nationality) |
||||||
|
.idNumber(idNumber) |
||||||
|
.build(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
package cn.linghang.mywust.core.parser.undergraduate.xpath; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p>本科生学生信息接口用到的xpath</p> |
||||||
|
* <p>看着挺唬人的,其实直接浏览器选择元素复制xpath就行了</p> |
||||||
|
* <p>这里的xpath只要网站UI不整什么花活就不会出问题</p> |
||||||
|
* |
||||||
|
* @author lensfrex |
||||||
|
* @create 2022-10-22 22:16 |
||||||
|
*/ |
||||||
|
public class StudentInfoXpath { |
||||||
|
public static final String STUDENT_NUMBER = "//*[@id=\"xjkpTable\"]/tbody/tr[3]/td[5]"; |
||||||
|
|
||||||
|
public static final String COLLEGE = "//*[@id=\"xjkpTable\"]/tbody/tr[3]/td[1]"; |
||||||
|
|
||||||
|
public static final String MAJOR = "//*[@id=\"xjkpTable\"]/tbody/tr[3]/td[2]"; |
||||||
|
|
||||||
|
public static final String CLASS = "//*[@id=\"xjkpTable\"]/tbody/tr[3]/td[4]"; |
||||||
|
|
||||||
|
public static final String NAME = "//*[@id=\"xjkpTable\"]/tbody/tr[4]/td[2]"; |
||||||
|
|
||||||
|
public static final String SEX = "//*[@id=\"xjkpTable\"]/tbody/tr[4]/td[4]"; |
||||||
|
|
||||||
|
public static final String BIRTHDAY = "//*[@id=\"xjkpTable\"]/tbody/tr[5]/td[2]"; |
||||||
|
|
||||||
|
public static final String HOMETOWN = "//*[@id=\"xjkpTable\"]/tbody/tr[7]/td[2]"; |
||||||
|
|
||||||
|
public static final String NATIONALITY = "//*[@id=\"xjkpTable\"]/tbody/tr[8]/td[4]"; |
||||||
|
|
||||||
|
public static final String ID_NUMBER = "//*[@id=\"xjkpTable\"]/tbody/tr[50]/td[4]"; |
||||||
|
} |
@ -1,8 +1,7 @@ |
|||||||
package cn.linghang.mywust.core.service.auth; |
package cn.linghang.mywust.core.request; |
||||||
|
|
||||||
import cn.linghang.mywust.core.api.UnionAuth; |
import cn.linghang.mywust.core.api.UnionAuth; |
||||||
import cn.linghang.mywust.network.HttpRequest; |
import cn.linghang.mywust.network.HttpRequest; |
||||||
import cn.linghang.mywust.network.RequestFactory; |
|
||||||
import cn.linghang.mywust.util.StringUtil; |
import cn.linghang.mywust.util.StringUtil; |
||||||
|
|
||||||
import java.nio.charset.StandardCharsets; |
import java.nio.charset.StandardCharsets; |
@ -1,19 +1,22 @@ |
|||||||
package cn.linghang.mywust.core.service.undergraduate; |
package cn.linghang.mywust.core.request; |
||||||
|
|
||||||
import cn.linghang.mywust.core.api.Bkjx; |
import cn.linghang.mywust.core.api.Bkjx; |
||||||
import cn.linghang.mywust.network.HttpRequest; |
import cn.linghang.mywust.network.HttpRequest; |
||||||
import cn.linghang.mywust.network.RequestFactory; |
|
||||||
import cn.linghang.mywust.util.StringUtil; |
import cn.linghang.mywust.util.StringUtil; |
||||||
|
|
||||||
import java.nio.charset.StandardCharsets; |
import java.nio.charset.StandardCharsets; |
||||||
import java.util.HashMap; |
import java.util.HashMap; |
||||||
import java.util.Map; |
import java.util.Map; |
||||||
|
|
||||||
public class BkjxAuthRequestFactory extends RequestFactory { |
public class BkjxRequestFactory extends RequestFactory { |
||||||
public static HttpRequest sessionCookieRequest(String serviceTicket) { |
public static HttpRequest sessionCookieRequest(String serviceTicket) { |
||||||
return makeHttpRequest(String.format(Bkjx.BKJX_SESSION_COOKIE_API, serviceTicket)); |
return makeHttpRequest(String.format(Bkjx.BKJX_SESSION_COOKIE_API, serviceTicket)); |
||||||
} |
} |
||||||
|
|
||||||
|
public static HttpRequest studentInfoRequest(String cookies) { |
||||||
|
return makeHttpRequest(Bkjx.BKJX_STUDENT_INFO_API, null, cookies); |
||||||
|
} |
||||||
|
|
||||||
public static class Legacy { |
public static class Legacy { |
||||||
public static HttpRequest dataStringRequest() { |
public static HttpRequest dataStringRequest() { |
||||||
return makeHttpRequest(Bkjx.Legacy.BKJX_DATA_STRING_API); |
return makeHttpRequest(Bkjx.Legacy.BKJX_DATA_STRING_API); |
@ -1,10 +1,9 @@ |
|||||||
package cn.linghang.mywust.core.service.library; |
package cn.linghang.mywust.core.request; |
||||||
|
|
||||||
import cn.linghang.mywust.core.api.Library; |
import cn.linghang.mywust.core.api.Library; |
||||||
import cn.linghang.mywust.network.HttpRequest; |
import cn.linghang.mywust.network.HttpRequest; |
||||||
import cn.linghang.mywust.network.RequestFactory; |
|
||||||
|
|
||||||
public class LibraryAuthRequestFactory extends RequestFactory { |
public class LibraryRequestFactory extends RequestFactory { |
||||||
public static HttpRequest sessionCookieRequest(String serviceTicket) { |
public static HttpRequest sessionCookieRequest(String serviceTicket) { |
||||||
return makeHttpRequest(String.format(Library.LIBRARY_SESSION_COOKIE_API, serviceTicket)); |
return makeHttpRequest(String.format(Library.LIBRARY_SESSION_COOKIE_API, serviceTicket)); |
||||||
} |
} |
@ -1,4 +1,6 @@ |
|||||||
package cn.linghang.mywust.network; |
package cn.linghang.mywust.core.request; |
||||||
|
|
||||||
|
import cn.linghang.mywust.network.HttpRequest; |
||||||
|
|
||||||
import java.net.URL; |
import java.net.URL; |
||||||
|
|
@ -0,0 +1,4 @@ |
|||||||
|
package cn.linghang.mywust.core.service.library; |
||||||
|
|
||||||
|
public class LibraryService { |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
package cn.linghang.mywust.core.service.undergraduate; |
||||||
|
|
||||||
|
import cn.linghang.mywust.core.exception.CookieInvalidException; |
||||||
|
import cn.linghang.mywust.core.exception.HtmlPageParseException; |
||||||
|
import cn.linghang.mywust.core.parser.undergraduate.StudentInfoPageParser; |
||||||
|
import cn.linghang.mywust.core.request.BkjxRequestFactory; |
||||||
|
import cn.linghang.mywust.core.util.BkjxUtil; |
||||||
|
import cn.linghang.mywust.model.undergrade.StudentInfo; |
||||||
|
import cn.linghang.mywust.network.HttpRequest; |
||||||
|
import cn.linghang.mywust.network.HttpResponse; |
||||||
|
import cn.linghang.mywust.network.RequestClientOption; |
||||||
|
import cn.linghang.mywust.network.Requester; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
public class JwcService { |
||||||
|
private static final Logger log = LoggerFactory.getLogger(JwcService.class); |
||||||
|
|
||||||
|
private final Requester requester; |
||||||
|
|
||||||
|
private final StudentInfoPageParser studentInfoPageParser; |
||||||
|
|
||||||
|
public JwcService(Requester requester, StudentInfoPageParser studentInfoPageParser) { |
||||||
|
this.requester = requester; |
||||||
|
this.studentInfoPageParser = studentInfoPageParser; |
||||||
|
} |
||||||
|
|
||||||
|
public String getStudentInfoPage(String cookies, RequestClientOption requestOption) throws IOException, CookieInvalidException { |
||||||
|
HttpRequest request = BkjxRequestFactory.studentInfoRequest(cookies); |
||||||
|
HttpResponse response = requester.get(request, requestOption); |
||||||
|
|
||||||
|
// 检查响应是否正确
|
||||||
|
if (response.getBody() == null || |
||||||
|
response.getStatusCode() != HttpResponse.HTTP_OK || |
||||||
|
BkjxUtil.checkLoginFinger(response.getBody())) { |
||||||
|
|
||||||
|
throw new CookieInvalidException(); |
||||||
|
} |
||||||
|
|
||||||
|
return new String(response.getBody()); |
||||||
|
} |
||||||
|
|
||||||
|
public StudentInfo getStudentInfo(String cookies, RequestClientOption requestOption) throws IOException, CookieInvalidException, HtmlPageParseException { |
||||||
|
String studentInfoPage = this.getStudentInfoPage(cookies, requestOption); |
||||||
|
|
||||||
|
return studentInfoPageParser.parse(studentInfoPage); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package cn.linghang.mywust.core.util; |
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
|
||||||
|
/** |
||||||
|
* 专属于bkjx(本科教学)系统服务的工具类 |
||||||
|
*/ |
||||||
|
public class BkjxUtil { |
||||||
|
// 在“Bkjx”系统里边如果收到的响应开头是这个的话多半是cookie无效了,需要重新登录获取cookie
|
||||||
|
private static final byte[] LOGIN_MESSAGE_RESPONSE_FINGER = "<script languge='javascript'>".getBytes(StandardCharsets.UTF_8); |
||||||
|
|
||||||
|
/** |
||||||
|
* <p>通过粗暴地比较响应字节前几个字符是否为登录跳转特征字符判断是否需要重新登录</p> |
||||||
|
* <p>对于null对象,一律认为不需要</p> |
||||||
|
* |
||||||
|
* @param response 响应的字节 |
||||||
|
* @return 是否需要重新登录 |
||||||
|
*/ |
||||||
|
public static boolean checkLoginFinger(byte[] response) { |
||||||
|
if (response == null) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; i < LOGIN_MESSAGE_RESPONSE_FINGER.length; i++) { |
||||||
|
if (LOGIN_MESSAGE_RESPONSE_FINGER[i] != response[i]) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
package cn.linghang.mywust.model.undergrade; |
||||||
|
|
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Builder; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.NoArgsConstructor; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p>学生数据实体类</p> |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
@Builder |
||||||
|
@NoArgsConstructor |
||||||
|
@AllArgsConstructor |
||||||
|
public class StudentInfo { |
||||||
|
private String studentNumber; |
||||||
|
private String name; |
||||||
|
private String college; |
||||||
|
private String major; |
||||||
|
private String clazz; |
||||||
|
private String birthday; |
||||||
|
private String sex; |
||||||
|
private String nationality; |
||||||
|
private String hometown; |
||||||
|
private String idNumber; |
||||||
|
} |
Loading…
Reference in new issue