实现HDU基础爬虫提交和查询结果

This commit is contained in:
Howie 2021-02-12 19:09:37 +08:00
parent 2631577541
commit 0fc7aa51d4
9 changed files with 230 additions and 49 deletions

View File

@ -97,11 +97,29 @@
</exclusion>
</exclusions>
</dependency>
<!-- Jsoup-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.13.1</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -149,9 +149,6 @@ public class JudgeController {
@PostMapping(value = "/remote-judge")
public CommonResult remoteJudge(@RequestBody ToJudge toJudge) {
/**
* TODO 在此调用远程判题
*/
// 发送消息
try {
String[] source = toJudge.getRemoteJudge().split("-");

View File

@ -16,6 +16,8 @@ import top.hcode.hoj.remoteJudge.task.RemoteJudgeStrategy;
import top.hcode.hoj.util.Constants;
import top.hcode.hoj.util.RedisUtils;
import java.util.Map;
@Slf4j
@Component
public class RemoteJudgeResultReceiver implements MessageListener {
@ -39,8 +41,12 @@ public class RemoteJudgeResultReceiver implements MessageListener {
return;
}
String result = remoteJudgeStrategy.result(submitId);
// TODO 获取对应的result ID修改到数据库
// TODO 获取对应的result修改到数据库
try {
Map<String, Object> result = remoteJudgeStrategy.result(submitId);
} catch (Exception e) {
log.error("获取结果出错------------>{}", e.getMessage());
}
}
}

View File

@ -42,10 +42,15 @@ public class RemoteJudgeSubmitReceiver implements MessageListener {
log.error("暂不支持该{}题库---------------->请求失败", remoteJudge);
return;
}
Long submitId = remoteJudgeStrategy.submit(pid, language, userCode);
// TODO 提交失败 修改状态 STATUS_NOT_SUBMITTED
Long submitId = -1L;
try {
submitId = remoteJudgeStrategy.submit(pid, language, userCode);
} catch (Exception e) {
log.error("网络错误,提交失败");
}
// TODO 提交失败 前端手动按按钮再次提交 修改状态 STATUS_NOT_SUBMITTED
if (submitId < 0) {
log.error("提交失败---------------->获取不到提交ID");
log.error("网络错误---------------->获取不到提交ID");
return;
}
try {

View File

@ -1,53 +1,147 @@
package top.hcode.hoj.remoteJudge.task.Impl;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReUtil;
import jdk.nashorn.internal.runtime.regexp.RegExp;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Connection;
import org.jsoup.helper.Validate;
import top.hcode.hoj.remoteJudge.task.RemoteJudgeStrategy;
import top.hcode.hoj.util.Constants;
import top.hcode.hoj.util.JsoupUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
public class HduJudge implements RemoteJudgeStrategy {
public static final String host = "acm.hdu.edu.cn";
public static final String host = "http://acm.hdu.edu.cn";
public static final String loginUrl = "/userloginex.php?action=login";
public static final String submitUrl = "/submit.php?action=submit";
public static final String statusUrl = "/status.php?user=%s&pid=%d";
public static final String queryUrl = "/status.php?first=%d";
public static final String errorUrl = "/viewerror.php?rid=%d";
/**
* TODO 提交题目
*
* @param problemId 提交的题目id
* @param language
* @param userCode 用户代码
* @return
*/
@Override
public Long submit(Long problemId, String language, String userCode) {
public Long submit(Long problemId, String language, String userCode) throws Exception {
if (problemId == null || userCode == null) {
return -1L;
}
return null;
}
@Override
public String result(Long submitId) {
return null;
}
@Override
public Map<String, String> getLoginCookie() {
try {
Connection connection = JsoupUtils.getConnectionFromUrl(host + loginUrl, null, null);
Connection.Response response = JsoupUtils.postResponse(connection, MapUtil
.builder(new HashMap<String, String>())
.put("username", "12")
.put("userpass", "").map());
return response.cookies();
} catch (IOException e) {
log.error("网络错误");
Map<String, String> loginCookie = getLoginCookie();
Connection connection = JsoupUtils.getConnectionFromUrl(host + submitUrl, null, loginCookie);
Connection.Response response = JsoupUtils.postResponse(connection, MapUtil
.builder(new HashMap<String, String>())
.put("check", "0")
.put("language", getLanguage(language))
.put("problemid", String.valueOf(problemId))
.put("usercode", userCode)
.map());
System.out.println("提交代码的状态码:-------" + response.statusCode());
if (response.statusCode() != 200) {
log.error("提交题目失败");
return -1L;
}
return null;
// 获取提交的题目id
Long maxRunId = getMaxRunId(connection, "2018030402055", problemId);
System.out.println(maxRunId);
return maxRunId;
}
@Override
public Map<String, Object> result(Long submitId) throws Exception {
String url = host + String.format(queryUrl, submitId);
System.out.println(url);
Connection connection = JsoupUtils.getConnectionFromUrl(url, null, null);
Connection.Response response = JsoupUtils.getResponse(connection, null);
// 1提交时间 2结果 3执行时间 4执行空间 5代码长度
// 一般情况下 代码长度和提交时间不需要想要也行自行添加
Pattern pattern = Pattern.compile(">" + submitId + "</td><td>([\\s\\S]*?)</td><td>([\\s\\S]*?)</td><td>[\\s\\S]*?</td><td>(\\d*?)MS</td><td>(\\d*?)K</td><td>(\\d*?)B</td>");
Matcher matcher = pattern.matcher(response.body());
// System.out.println(response.body());
// 找到时
Validate.isTrue(matcher.find());
String rawStatus = matcher.group(2).replaceAll("<[\\s\\S]*?>", "").trim();
System.out.println(rawStatus);
Constants.Judge statusType = statusTypeMap.get(rawStatus);
// 返回的结果map
Map<String, Object> result = MapUtil.builder(new HashMap<String, Object>())
.put("status", statusType).build();
// 获取其他信息
String executionTime = matcher.group(3);
result.put("executionTime", executionTime);
String executionMemory = matcher.group(4);
result.put("executionMemory", executionMemory);
// 如果CE了则还需要获得错误信息
if (statusType == Constants.Judge.STATUS_COMPILE_ERROR) {
connection.url(host + String.format(errorUrl, submitId));
response = JsoupUtils.getResponse(connection, null);
String compilationErrorInfo = ReUtil.get("(<pre>[\\s\\S]*?</pre>)", response.body(), 1);
result.put("compilationErrorInfo", compilationErrorInfo);
}
return result;
}
@Override
public Map<String, String> getLoginCookie() throws Exception {
Connection connection = JsoupUtils.getConnectionFromUrl(host + loginUrl, null, null);
Connection.Response response = JsoupUtils.postResponse(connection, MapUtil
.builder(new HashMap<String, String>())
// TODO 添加账号密码 暂时写死测试后续将在队列中获取空闲账号
.put("username", "11")
.put("userpass", "11").map());
return response.cookies();
}
@Override
public String getLanguage(String language) {
switch (language) {
case "G++":
return "0";
case "GCC":
return "1";
case "C++":
return "2";
case "C":
return "3";
case "Pascal":
return "4";
case "Java":
return "5";
case "C#":
return "6";
default:
// TODO 抛出没有这个语言的异常
return null;
}
}
public Long getMaxRunId(Connection connection, String userName, Long problemId) throws Exception {
String url = String.format(statusUrl, userName, problemId);
connection.url(host + url);
Connection.Response response = JsoupUtils.getResponse(connection, null);
Matcher matcher = Pattern.compile("<td height=22px>(\\d+)").matcher(response.body());
return matcher.find() ? Long.parseLong(matcher.group(1)) : -1L;
}
// TODO 添加结果对应的状态
private static final Map<String, Constants.Judge> statusTypeMap = new HashMap<String, Constants.Judge>() {
{
put("Submitted", Constants.Judge.STATUS_SUBMITTING);
put("Accepted", Constants.Judge.STATUS_ACCEPTED);
put("Wrong Answer", Constants.Judge.STATUS_WRONG_ANSWER);
put("Compilation Error", Constants.Judge.STATUS_COMPILE_ERROR);
put("Time Limit Exceeded", Constants.Judge.STATUS_TIME_LIMIT_EXCEEDED);
}
};
}

View File

@ -1,6 +1,7 @@
package top.hcode.hoj.remoteJudge.task;
import java.io.IOException;
import java.util.Map;
@ -14,14 +15,16 @@ public interface RemoteJudgeStrategy {
* @param userCode 用户代码
* @return 返回对应题库的提交Id
*/
Long submit(Long problemId, String language, String userCode);
Long submit(Long problemId, String language, String userCode) throws Exception;
/**
* @param submitId 题库的提交ID
* @return 返回结果
*/
String result(Long submitId);
Map<String, Object> result(Long submitId) throws Exception;
Map<String, String> getLoginCookie();
Map<String, String> getLoginCookie() throws Exception;
String getLanguage(String language);
}

View File

@ -23,10 +23,11 @@ public class JsoupUtils {
Connection connection = Jsoup.connect(url);
// 设置用户代理
connection.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36");
// 设置超时时间5
connection.timeout(5000);
// 设置超时时间10
connection.timeout(10000);
// 设置cookie保存信息
connection.cookies(cookies);
if (cookies != null)
connection.cookies(cookies);
// 设置请求头
if (headers != null) {
connection.headers(headers);
@ -35,21 +36,14 @@ public class JsoupUtils {
}
public static Connection.Response postResponse(Connection connection, Map<String, String> postData) throws IOException {
//添加参数
if (postData != null) {
for (Map.Entry<String, String> entry : postData.entrySet()) {
connection.data(entry.getKey(), entry.getValue());
}
}
connection.data(postData);
return connection.method(Connection.Method.POST).execute();
}
public static Connection.Response getResponse(Connection connection, Map<String, String> postData) throws IOException {
public static Connection.Response getResponse(Connection connection, Map<String, String> getData) throws IOException {
//添加参数
if (postData != null) {
for (Map.Entry<String, String> entry : postData.entrySet()) {
connection.data(entry.getKey(), entry.getValue());
}
if (getData != null) {
connection.data(getData);
}
return connection.method(Connection.Method.GET).execute();
}

View File

@ -0,0 +1,41 @@
package top.hcode.hoj;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import top.hcode.hoj.remoteJudge.task.Impl.HduJudge;
import java.io.IOException;
import java.util.Map;
@SpringBootTest
public class JudgeServerApplicationTests {
@Test
void test1() {
HduJudge hduJudge = new HduJudge();
String code = "#include<iostream>\n" +
"using namespace std;\n" +
"\n" +
"int main()\n" +
"{\n" +
"\tint t;\n" +
"\tcin >> t;\n" +
"\twhile (t--) {\n" +
"\t\tint a, b;\n" +
"\t\tcin >> a >> b;\n" +
"\t\tcout << a + b;\n" +
"\t\tif (t == 0){\n" +
"\t\t\tcout << endl;\n" +
"\t\t} else {\n" +
"\t\t\tcout << endl;\n" +
"\t\t}\n" +
"\t}\n" +
"\treturn 1;\n" +
"}";
try {
Long submit = hduJudge.submit(1090L, "G++", code);
System.out.println(submit);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,23 @@
package top.hcode.hoj.remoteJudge.task.Impl;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
class HduJudgeTest {
@Test
void getLoginCookie() {
HduJudge hduJudge = new HduJudge();
try {
Map<String, Object> submit = hduJudge.result(35329033L);
System.out.println(submit);
} catch (Exception e) {
e.printStackTrace();
}
}
}