记得以前刷hdu的时候总是发现有人能一分钟内提交很多次 而且还ac 感觉不可思议。后来百度搜了搜 原来是网络爬虫
带着这一届acm成员集训的时候有成员说hdu炸了 一直判断中 我就说是爬虫。。。然后就想了想 自己能不能写而且要用java写
结果一天没吃饭(因为感觉我能做出来)。。。直到晚上6点多 测试一个数据 A了 那种感觉 无法用言语表达。。。比吃了任何东西都快乐。
一直对自己的自学能力很有自信的 可怕的就是没有方向 四处乱撞 唉
给做题的人说声对不起 我知道这是不道德的行为 我已经让线程睡眠了10S 就是为了不耽误你们做题
仅仅是为了学习 如果为了刷排名 不推荐啊~~
AC率不高的原因是提交时间间隔太短 以至于代码结果还没出来就提交下一道了 。。。不想改了。
进入正题:
1.遍历所有题目,使用url模拟http的get请求向服务器发送页面请求
2.获得返回页面源码 使用正则表达式筛选出csdn网站的网址放入集合
3.同1,进入筛选出的csdn网站
4.从csdn网站的源码中筛选出所需要的试题代码答案。
5.对获得的代码答案进行编码处理(具体HTML元素符号的换成> <...等)
6.使用http的post请求提交向hdu网站提交代码。
7.最后提交后进行检测 如果是Accept就执行下一道题目 否则遍历集合中的下一个网址
具体做法
1.遍历所有题目,使用url模拟http的get请求向服务器发送页面请求
-
- public static String sendGet(String url) {
-
- String res = "";
-
- BufferedReader in = null;
- try {
-
- URL readURL = new URL(url);
-
- <span style="white-space:pre"> </span>try {
- trustAllHttpsCertificates();
- } catch (Exception e) {
-
- e.printStackTrace();
- }
- <span style="white-space:pre"> </span> HttpsURLConnection.setDefaultHostnameVerifier(hv);
-
- URLConnection connection = readURL.openConnection();
- connection.setRequestProperty("User-Agent",
- "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
-
- connection.connect();
-
- in = new BufferedReader(new InputStreamReader(
- connection.getInputStream()));
-
- String line = "";
- while ((line = in.readLine()) != null) {
- res += line;
- res+="\n";
- }
- } catch (MalformedURLException e) {
-
- e.printStackTrace();
- } catch (IOException e) {
-
- e.printStackTrace();
- } finally {
- try {
- if (in != null) {
- in.close();
- }
- } catch (IOException e) {
-
- e.printStackTrace();
- }
- }
- return res;
- }
大家可以看到我上面有两个标记mark1 ,mark1.其实第一天我没有加这两句话 也能够访问360搜索的可是第二天突然出现了 sun.security.validator.ValidatorException: PKIX path building failed 这个异常
百度搜索说是证书的问题 加上两个方法就OK了。
- static HostnameVerifier hv = new HostnameVerifier() {
- public boolean verify(String urlHostName, SSLSession session) {
- System.out.println("Warning: URL Host: " + urlHostName + " vs. "
- + session.getPeerHost());
- return true;
- }
- };
-
- private static void trustAllHttpsCertificates() throws Exception {
- javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];
- javax.net.ssl.TrustManager tm = new miTM();
- trustAllCerts[0] = tm;
- javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext
- .getInstance("SSL");
- sc.init(null, trustAllCerts, null);
- javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc
- .getSocketFactory());
- }
-
- static class miTM implements javax.net.ssl.TrustManager,
- javax.net.ssl.X509TrustManager {
- public java.security.cert.X509Certificate[] getAcceptedIssuers() {
- return null;
- }
-
- public boolean isServerTrusted(
- java.security.cert.X509Certificate[] certs) {
- return true;
- }
-
- public boolean isClientTrusted(
- java.security.cert.X509Certificate[] certs) {
- return true;
- }
-
- public void checkServerTrusted(
- java.security.cert.X509Certificate[] certs, String authType)
- throws java.security.cert.CertificateException {
- return;
- }
-
- public void checkClientTrusted(
- java.security.cert.X509Certificate[] certs, String authType)
- throws java.security.cert.CertificateException {
- return;
- }
- }
2.获得返回页面源码 使用正则表达式筛选出csdn网站的网址放入集合
至于我们为什么要访问360搜索而不用百度呢
是发现在使用百度搜索结果的源码中 对网址链接进行了加密 想来就是防止爬虫获得网址的。。
然后我们进入360搜索 查看搜索的源码 你发现了什么
对的 在红线上方不就是csdn博客的网址吗
使用正则表达式 很轻松的就能够得到了
哈哈 真是太聪明了 有人会说里面还有%2f等等什么的符号 仔细想想这是什么?http://blog.csdn.net 懂了吧
也就是把://等转换为了url编码 解码就好了 然后把筛选后的网址放入集合中即可
具体代码如下:
- public static void getCsdnUrl(String str) {
- String regex="http%3A%2F%2Fblog.csdn.net.{10,100}&q";
- Pattern p = Pattern.compile(regex);
- Matcher m = p.matcher(str);
- while (m.find()) {
- csdnURL.add(m.group(0).substring(0, m.group().length()-2));
- }
- }
url解码代码就是这一句:
- string=URLDecoder.decode(string, "utf-8");
3.同1,进入筛选出的csdn网站直接调用sendGet方法即可 返回Csdn页面源码
4.从csdn网站的源码中筛选出所需要的试题代码答案。
看到了我们需要的代码了吗?
同样使用强大的正则表达式筛选出我们需要的代码
在这里我们只选择#include开头的然后以</结束(一般以</pre>,也有以</textarea>的,也有其它的 所以我们就以</字符为结束)
具体代码如下:
-
- static String getCode(String str) {
- int pos = str.indexOf("#include");
- String res = "";
- if (pos == -1)
- return "";
- boolean isCode = false;
- for (int i = pos; i < str.length(); i++) {
- if (str.charAt(i) == '<' && str.charAt(i + 1) == '/')
- break;
- if (str.charAt(i) == 'i' && str.charAt(i + 1) == 'n'
- && str.charAt(i + 2) == 't' && str.charAt(i + 3) == ' '
- && str.charAt(i + 4) == 'm' && str.charAt(i + 5) == 'a'
- && str.charAt(i + 6) == 'i' && str.charAt(i + 7) == 'n') {
- isCode = true;
- }
- res += str.charAt(i);
- }
- if (isCode)
- return res;
- else
- return "";
- }
5.对获得的代码答案进行编码处理(具体HTML元素符号的换成> <...等)
然后我们把代码中出现的具有html符号的元素改为我们熟悉的符号
-
- static String replaceDiv(String str) {
- String res = "";
- int len = str.length();
- for (int i = 0; i < len; i++) {
- if (i + 3 < len && str.charAt(i) == '&' && str.charAt(i + 1) == 'l'
- && str.charAt(i + 2) == 't' && str.charAt(i + 3) == ';') {
- res += '<';
- i += 3;
- } else if (i + 3 < len && str.charAt(i) == '&'
- && str.charAt(i + 1) == 'g' && str.charAt(i + 2) == 't'
- && str.charAt(i + 3) == ';') {
- res += '>';
- i += 3;
- } else if (i + 1 < len && str.charAt(i) == '/'
- && str.charAt(i + 1) == 'n') {
- res += "\\n";
- i += 1;
- } else if (i + 4 < len && str.charAt(i) == '&'
- && str.charAt(i + 1) == 'a' && str.charAt(i + 2) == 'm'
- && str.charAt(i + 3) == 'p' && str.charAt(i + 4) == ';') {
- res += '&';
- i += 4;
- } else if (i + 5 < len && str.charAt(i) == '&'
- && str.charAt(i + 1) == 'q' && str.charAt(i + 2) == 'u'
- && str.charAt(i + 3) == 'o' && str.charAt(i + 4) == 't'
- && str.charAt(i + 5) == ';') {
- res += '\"';
- i += 5;
- } else if (str.charAt(i) == '&' && str.charAt(i + 1) == 'n'
- && str.charAt(i + 2) == 'b' && str.charAt(i + 3) == 's'
- && str.charAt(i + 4) == 'p' && str.charAt(i + 5) == ';') {
- res += ' ';
- i += 5;
- } else if (i + 4 < len && str.charAt(i) == '&'
- && str.charAt(i + 1) == '#' && str.charAt(i + 2) == '4'
- && str.charAt(i + 3) == '3' && str.charAt(i + 4) == ';') {
- res += '+';
- i += 4;
- } else if (i + 4 < len && str.charAt(i) == '&'
- && str.charAt(i + 1) == '#' && str.charAt(i + 2) == '3'
- && str.charAt(i + 3) == '9' && str.charAt(i + 4) == ';') {
- res += '\'';
- i += 4;
- } else if (i + 3 < len && str.charAt(i) == '<'
- && str.charAt(i + 1) == 'b' && str.charAt(i + 2) == 'r'
- && str.charAt(i + 3) == '>') {
- res += "\n";
- i += 3;
-
- } else if (i + 4 < len && str.charAt(i) == '&'
- && str.charAt(i + 1) == '#' && str.charAt(i + 2) == '1'
- && str.charAt(i + 3) == '6' && str.charAt(i + 4) == '0') {
- res += " ";
- i += 4;
- } else
- res += str.charAt(i);
- }
- return res;
- }
现在所有的准备工作都已经做好了就剩下向hdu提交了
6.使用http的post请求提交向hdu网站提交代码。
既然想提交 那么我们就要知道当我们点击了submit后发生了什么
post的网页数据是什么
在这里介绍给你一个神器 下载地址我会放在下面
用fiddler抓包,使用java的post请求模拟网页的post数据即可
举个例子:
这就是我提交一道题目后 使用用fiddler抓包 获取的数据
我们可以获得提交的题目是hdu1022 语言就是GCC usercode就是我们代码(url编码)
模拟http的post请求把抓到的数据比着葫芦画瓢再写一遍就好了
尤其是Cookie 重启一次浏览器 就要更改一下Cookie。否则提交不上哦
具体代码如下
-
- URL postUrl = new URL("http://acm.hdu.edu.cn/submit.php?action=submit");
-
- HttpURLConnection connection = (HttpURLConnection) postUrl
- .openConnection();
- connection.setDoOutput(true);
- connection.setDoInput(true);
-
- connection.setRequestMethod("POST");
-
-
- connection.setUseCaches(false);
-
- connection.setInstanceFollowRedirects(true);
-
-
- connection.setRequestProperty("Host", "acm.hdu.edu.cn");
- connection.setRequestProperty("Connection", "Keep-Alive");
- connection.setRequestProperty("Content-Length", code.length() + "");
- connection.setRequestProperty("Pragma", "no-cache");
- connection.setRequestProperty("Cache-Control", "no-cache");
- connection
- .setRequestProperty(
- "Accept",
- "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
- connection.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.8");
-
- connection.setRequestProperty("Content-Type",
- "application/x-www-form-urlencoded");
-
- connection
- .setRequestProperty(
- "User-Agent",
- "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.22 Safari/537.36 SE 2.X MetaSr 1.0");
- connection.setRequestProperty("DNT", "1");
- connection.setRequestProperty("Upgrade-Insecure-Requests"," 1");
- connection.setRequestProperty("Referer",
- "http://acm.hdu.edu.cn/submit.php?pid=" + problemID);
- connection.setRequestProperty("Accept-Encoding", "gzip, deflate");
- connection
- .setRequestProperty(
- "Cookie",
- "exesubmitlang=1; PHPSESSID=vla6d2llv1q25tipnva019stg4; CNZZDATA1254072405=1258731135-1470215877-%7C1470998268");
-
-
- connection.connect();
- DataOutputStream out = new DataOutputStream(
- connection.getOutputStream());
-
- String urlCode = URLEncoder.encode(code, "utf-8");
- String content = "check=0&problemid=" + problemID
- + "&language=0&usercode=" + urlCode;
- out.writeBytes(content);
- out.flush();
- out.close();
- BufferedReader reader = new BufferedReader(new InputStreamReader(
- connection.getInputStream()));
- reader.close();
- connection.disconnect();
7.最后提交后进行检测 如果是Accept就执行下一道题目 否则遍历集合中的下一个网址
检查我们提交的代码可以再http://acm.hdu.edu.cn/status.php这里查看
我们只要根据当前题号查找 然后在源代码中再根据正则表达式 判断判断是否Accepted 如果接受了就返回true 否则false
具体怎么做我就不说了 如果你看了上面的肯定能懂。
具体代码如下
- public static boolean checkRes(int problemID) {
- String url = "http://acm.hdu.edu.cn/status.php?first=&pid=" + problemID
- + "&user=&lang=0&status=0";
- String msg = Main.sendGet(url);
- String regex = "red>Accepted.{100,200}1142819049";
- Pattern p = Pattern.compile(regex);
- Matcher m = p.matcher(msg);
- boolean state = m.find();
- System.out.println(problemID + " " + state);
- return state;
- }
好了 大功告成。奉上一句话。。刷题一时爽,封号就OVER
我的这个账号已经被封了 才刷到5400多到 。。突然发现不能提交了 唉
在登录就是密码错误。。
在这里再给HDU道个歉 对不起,,仅仅是为了学习。希望能接受。
fiddler 神器下载
3 0