教你小小JAVA爬虫爬到HDU首页(只为学习)

来源:互联网 发布:java实现密钥进行加密 编辑:程序博客网 时间:2024/05/29 12:53

记得以前刷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请求向服务器发送页面请求

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. // JAVA模拟get方法访问网页  
  2. public static String sendGet(String url) {  
  3.     // 定义一个字符串存贮网页内容  
  4.     String res = "";  
  5.     // 定义一个缓冲字符输入流  
  6.     BufferedReader in = null;  
  7.     try {  
  8.         // 将string转换为URL对象  
  9.         URL readURL = new URL(url);  
  10.           
  11.            <span style="white-space:pre"> </span>try {  
  12.             trustAllHttpsCertificates();//mark1  
  13.         } catch (Exception e) {  
  14.             // TODO Auto-generated catch block  
  15.             e.printStackTrace();  
  16.         }    
  17.          <span style="white-space:pre">       </span>  HttpsURLConnection.setDefaultHostnameVerifier(hv); //mark1   
  18.         // 初始化一个链接到那个URL的链接  
  19.         URLConnection connection = readURL.openConnection();  
  20.         connection.setRequestProperty("User-Agent",  
  21.                     "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");  
  22.         // 开始实际的URL  
  23.         connection.connect();  
  24.         // 初始化bufferedReader输入流来读取URL的相应  
  25.         in = new BufferedReader(new InputStreamReader(  
  26.                 connection.getInputStream()));  
  27.         // 用来临时抓取到的每一行数据  
  28.         String line = "";  
  29.         while ((line = in.readLine()) != null) {  
  30.             res += line;  
  31.             res+="\n";  
  32.         }  
  33.     } catch (MalformedURLException e) {  
  34.         // TODO Auto-generated catch block  
  35.         e.printStackTrace();  
  36.     } catch (IOException e) {  
  37.         // TODO Auto-generated catch block  
  38.         e.printStackTrace();  
  39.     } finally {  
  40.         try {  
  41.             if (in != null) {  
  42.                 in.close();  
  43.             }  
  44.         } catch (IOException e) {  
  45.             // TODO Auto-generated catch block  
  46.             e.printStackTrace();  
  47.         }  
  48.     }  
  49.     return res;//返回网页源码  
  50. }  
大家可以看到我上面有两个标记mark1 ,mark1.其实第一天我没有加这两句话 也能够访问360搜索的

可是第二天突然出现了 sun.security.validator.ValidatorException: PKIX path building failed  这个异常

百度搜索说是证书的问题  加上两个方法就OK了。

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. static HostnameVerifier hv = new HostnameVerifier() {    
  2.         public boolean verify(String urlHostName, SSLSession session) {    
  3.             System.out.println("Warning: URL Host: " + urlHostName + " vs. "    
  4.                                + session.getPeerHost());    
  5.             return true;    
  6.         }    
  7.     };    
  8.         
  9.     private static void trustAllHttpsCertificates() throws Exception {    
  10.         javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];    
  11.         javax.net.ssl.TrustManager tm = new miTM();    
  12.         trustAllCerts[0] = tm;    
  13.         javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext    
  14.                 .getInstance("SSL");    
  15.         sc.init(null, trustAllCerts, null);    
  16.         javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc    
  17.                 .getSocketFactory());    
  18.     }    
  19.     
  20.     static class miTM implements javax.net.ssl.TrustManager,    
  21.             javax.net.ssl.X509TrustManager {    
  22.         public java.security.cert.X509Certificate[] getAcceptedIssuers() {    
  23.             return null;    
  24.         }    
  25.     
  26.         public boolean isServerTrusted(    
  27.                 java.security.cert.X509Certificate[] certs) {    
  28.             return true;    
  29.         }    
  30.     
  31.         public boolean isClientTrusted(    
  32.                 java.security.cert.X509Certificate[] certs) {    
  33.             return true;    
  34.         }    
  35.     
  36.         public void checkServerTrusted(    
  37.                 java.security.cert.X509Certificate[] certs, String authType)    
  38.                 throws java.security.cert.CertificateException {    
  39.             return;    
  40.         }    
  41.     
  42.         public void checkClientTrusted(    
  43.                 java.security.cert.X509Certificate[] certs, String authType)    
  44.                 throws java.security.cert.CertificateException {    
  45.             return;    
  46.         }    
  47.     }    


2.获得返回页面源码 使用正则表达式筛选出csdn网站的网址放入集合

至于我们为什么要访问360搜索而不用百度呢 

是发现在使用百度搜索结果的源码中 对网址链接进行了加密  想来就是防止爬虫获得网址的。。

然后我们进入360搜索 查看搜索的源码  你发现了什么


对的  在红线上方不就是csdn博客的网址吗

使用正则表达式 很轻松的就能够得到了

哈哈  真是太聪明了  有人会说里面还有%2f等等什么的符号  仔细想想这是什么?http://blog.csdn.net  懂了吧

也就是把://等转换为了url编码  解码就好了  然后把筛选后的网址放入集合中即可

具体代码如下:

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public static void getCsdnUrl(String str) {  
  2.         String regex="http%3A%2F%2Fblog.csdn.net.{10,100}&q";  
  3.         Pattern p = Pattern.compile(regex);  
  4.         Matcher m = p.matcher(str);  
  5.         while (m.find()) {  
  6.             csdnURL.add(m.group(0).substring(0, m.group().length()-2));  
  7.         }  
  8.     }  
url解码代码就是这一句:

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. string=URLDecoder.decode(string, "utf-8");  

3.同1,进入筛选出的csdn网站

直接调用sendGet方法即可 返回Csdn页面源码

4.从csdn网站的源码中筛选出所需要的试题代码答案。


看到了我们需要的代码了吗?

同样使用强大的正则表达式筛选出我们需要的代码

在这里我们只选择#include开头的然后以</结束(一般以</pre>,也有以</textarea>的,也有其它的 所以我们就以</字符为结束)

具体代码如下:

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. // 从网页源代码中获取我们需要的试题代码  
  2.     static String getCode(String str) {  
  3.         int pos = str.indexOf("#include");  
  4.         String res = "";  
  5.         if (pos == -1)  
  6.             return "";  
  7.         boolean isCode = false;  
  8.         for (int i = pos; i < str.length(); i++) {  
  9.             if (str.charAt(i) == '<' && str.charAt(i + 1) == '/')  
  10.                 break;  
  11.             if (str.charAt(i) == 'i' && str.charAt(i + 1) == 'n'  
  12.                     && str.charAt(i + 2) == 't' && str.charAt(i + 3) == ' '  
  13.                     && str.charAt(i + 4) == 'm' && str.charAt(i + 5) == 'a'  
  14.                     && str.charAt(i + 6) == 'i' && str.charAt(i + 7) == 'n') {  
  15.                 isCode = true;  
  16.             }  
  17.             res += str.charAt(i);  
  18.         }  
  19.         if (isCode)  
  20.             return res;  
  21.         else  
  22.             return "";  
  23.     }  
5.对获得的代码答案进行编码处理(具体HTML元素符号的换成> <...等)
然后我们把代码中出现的具有html符号的元素改为我们熟悉的符号

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. // 对代码进行转换(HTML元素换成我们熟悉的字符)  
  2.     static String replaceDiv(String str) {  
  3.         String res = "";  
  4.         int len = str.length();  
  5.         for (int i = 0; i < len; i++) {  
  6.             if (i + 3 < len && str.charAt(i) == '&' && str.charAt(i + 1) == 'l'  
  7.                     && str.charAt(i + 2) == 't' && str.charAt(i + 3) == ';') {  
  8.                 res += '<';  
  9.                 i += 3;  
  10.             } else if (i + 3 < len && str.charAt(i) == '&'  
  11.                     && str.charAt(i + 1) == 'g' && str.charAt(i + 2) == 't'  
  12.                     && str.charAt(i + 3) == ';') {  
  13.                 res += '>';  
  14.                 i += 3;  
  15.             } else if (i + 1 < len && str.charAt(i) == '/'  
  16.                     && str.charAt(i + 1) == 'n') {  
  17.                 res += "\\n";  
  18.                 i += 1;  
  19.             } else if (i + 4 < len && str.charAt(i) == '&'  
  20.                     && str.charAt(i + 1) == 'a' && str.charAt(i + 2) == 'm'  
  21.                     && str.charAt(i + 3) == 'p' && str.charAt(i + 4) == ';') {  
  22.                 res += '&';  
  23.                 i += 4;  
  24.             } else if (i + 5 < len && str.charAt(i) == '&'  
  25.                     && str.charAt(i + 1) == 'q' && str.charAt(i + 2) == 'u'  
  26.                     && str.charAt(i + 3) == 'o' && str.charAt(i + 4) == 't'  
  27.                     && str.charAt(i + 5) == ';') {  
  28.                 res += '\"';  
  29.                 i += 5;  
  30.             } else if (str.charAt(i) == '&' && str.charAt(i + 1) == 'n'  
  31.                     && str.charAt(i + 2) == 'b' && str.charAt(i + 3) == 's'  
  32.                     && str.charAt(i + 4) == 'p' && str.charAt(i + 5) == ';') {  
  33.                 res += ' ';  
  34.                 i += 5;  
  35.             } else if (i + 4 < len && str.charAt(i) == '&'  
  36.                     && str.charAt(i + 1) == '#' && str.charAt(i + 2) == '4'  
  37.                     && str.charAt(i + 3) == '3' && str.charAt(i + 4) == ';') {  
  38.                 res += '+';  
  39.                 i += 4;  
  40.             } else if (i + 4 < len && str.charAt(i) == '&'  
  41.                     && str.charAt(i + 1) == '#' && str.charAt(i + 2) == '3'  
  42.                     && str.charAt(i + 3) == '9' && str.charAt(i + 4) == ';') {  
  43.                 res += '\'';  
  44.                 i += 4;  
  45.             } else if (i + 3 < len && str.charAt(i) == '<'  
  46.                     && str.charAt(i + 1) == 'b' && str.charAt(i + 2) == 'r'  
  47.                     && str.charAt(i + 3) == '>') {  
  48.                 res += "\n";  
  49.                 i += 3;  
  50.   
  51.             } else if (i + 4 < len && str.charAt(i) == '&'  
  52.                     && str.charAt(i + 1) == '#' && str.charAt(i + 2) == '1'  
  53.                     && str.charAt(i + 3) == '6' && str.charAt(i + 4) == '0') {  
  54.                 res += " ";  
  55.                 i += 4;  
  56.             } else  
  57.                 res += str.charAt(i);  
  58.         }  
  59.         return res;  
  60.     }  

现在所有的准备工作都已经做好了

就剩下向hdu提交了

6.使用http的post请求提交向hdu网站提交代码。

既然想提交 那么我们就要知道当我们点击了submit后发生了什么 

post的网页数据是什么

在这里介绍给你一个神器  下载地址我会放在下面

用fiddler抓包,使用java的post请求模拟网页的post数据即可

举个例子:




这就是我提交一道题目后 使用用fiddler抓包 获取的数据

我们可以获得提交的题目是hdu1022  语言就是GCC usercode就是我们代码(url编码)

模拟http的post请求把抓到的数据比着葫芦画瓢再写一遍就好了

尤其是Cookie  重启一次浏览器 就要更改一下Cookie。否则提交不上哦

具体代码如下

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. // Post请求的url  
  2.         URL postUrl = new URL("http://acm.hdu.edu.cn/submit.php?action=submit");  
  3.         // 打开连接  
  4.         HttpURLConnection connection = (HttpURLConnection) postUrl  
  5.                 .openConnection();  
  6.         connection.setDoOutput(true);  
  7.         connection.setDoInput(true);  
  8.         // 默认是 GET方式  
  9.         connection.setRequestMethod("POST");  
  10.   
  11.         // Post 请求不能使用缓存  
  12.         connection.setUseCaches(false);  
  13.   
  14.         connection.setInstanceFollowRedirects(true);  
  15.   
  16.         // 配置http post请求的head  
  17.         connection.setRequestProperty("Host""acm.hdu.edu.cn");  
  18.         connection.setRequestProperty("Connection""Keep-Alive");  
  19.         connection.setRequestProperty("Content-Length", code.length() + "");  
  20.         connection.setRequestProperty("Pragma""no-cache");  
  21.         connection.setRequestProperty("Cache-Control""no-cache");  
  22.         connection  
  23.                 .setRequestProperty(  
  24.                         "Accept",  
  25.                         "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");  
  26.         connection.setRequestProperty("Accept-Language""zh-CN,zh;q=0.8");  
  27.   
  28.         connection.setRequestProperty("Content-Type",  
  29.                 "application/x-www-form-urlencoded");  
  30.   
  31.         connection  
  32.                 .setRequestProperty(  
  33.                         "User-Agent",  
  34.                         "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");  
  35.         connection.setRequestProperty("DNT""1");  
  36.         connection.setRequestProperty("Upgrade-Insecure-Requests"," 1");  
  37.         connection.setRequestProperty("Referer",  
  38.                 "http://acm.hdu.edu.cn/submit.php?pid=" + problemID);  
  39.         connection.setRequestProperty("Accept-Encoding""gzip, deflate");  
  40.         connection  
  41.                 .setRequestProperty(  
  42.                         "Cookie",  
  43.                         "exesubmitlang=1; PHPSESSID=vla6d2llv1q25tipnva019stg4; CNZZDATA1254072405=1258731135-1470215877-%7C1470998268");  
  44.   
  45.         // 连接  
  46.         connection.connect();  
  47.         DataOutputStream out = new DataOutputStream(  
  48.                 connection.getOutputStream());  
  49.         // 使code转换为网络编码  
  50.         String urlCode = URLEncoder.encode(code, "utf-8");  
  51.         String content = "check=0&problemid=" + problemID  
  52.                 + "&language=0&usercode=" + urlCode;  
  53.         out.writeBytes(content);  
  54.         out.flush();  
  55.         out.close();  
  56.         BufferedReader reader = new BufferedReader(new InputStreamReader(  
  57.                 connection.getInputStream()));  
  58.         reader.close();  
  59.         connection.disconnect();  

7.最后提交后进行检测 如果是Accept就执行下一道题目 否则遍历集合中的下一个网址

检查我们提交的代码可以再http://acm.hdu.edu.cn/status.php这里查看

我们只要根据当前题号查找 然后在源代码中再根据正则表达式 判断判断是否Accepted 如果接受了就返回true  否则false

具体怎么做我就不说了 如果你看了上面的肯定能懂。

具体代码如下

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public static boolean checkRes(int problemID) {  
  2.         String url = "http://acm.hdu.edu.cn/status.php?first=&pid=" + problemID  
  3.                 + "&user=&lang=0&status=0";  
  4.         String msg = Main.sendGet(url);  
  5.         String regex = "red>Accepted.{100,200}1142819049";  
  6.         Pattern p = Pattern.compile(regex);  
  7.         Matcher m = p.matcher(msg);  
  8.         boolean state = m.find();  
  9.         System.out.println(problemID + " " + state);  
  10.         return state;  
  11.     }  

好了  大功告成。

奉上一句话。。刷题一时爽,封号就OVER

我的这个账号已经被封了  才刷到5400多到 。。突然发现不能提交了   唉 

在登录就是密码错误。。

在这里再给HDU道个歉  对不起,,仅仅是为了学习。希望能接受。

fiddler 神器下载


3 0
原创粉丝点击