暑假集训虽然很快乐,偶尔也会比较枯燥,,这个时候就需要自娱自乐...

然后看hdu的排行榜发现,除了一些是虚拟测评机的账号以外,有几个都是AC自动机器人

然后发现有一位作者是用网页填表然后按钮模拟,,,默默噗噗的笑了。。。

先来晒一下排行榜

要模拟网页,,当然POST大法好啊,直接模拟发送POST数据不就好了咩,,搞填表啥的多麻烦,完全可以写一个程序后台自动跑。

然后他说了一句AC率能达到50%以上的爬虫也是挺吊的,,于是激起了我试一试的决心(我是不是很wuliao)...

先解释一下POST大法是啥。。。。

貌似研究POST的话,,弄易语言的可能研究的会比较多,很多人会利用POST来实现注册机,采集机,发贴机等等,,这里面的利益关系也是很深的

但是易语言弄POST也会遇到一些局限,,毕竟是语言的局限,再怎样也无法跟C#大法来比较,虽说C#用来弄单纯的POST略有浪费。。。

网页与网页服务器是利用http协议传输数据,包括两种,一种是GET一种是POST

模拟浏览器的点击啥的,,其实最终的目的是让浏览器解析网页,然后发送对应的数据包反馈给服务器

然而我们为什么不直接发送对应的数据包给服务器呢?这样就不关浏览器的事情了,也增加了程序的鲁棒性

对于整个程序总体的思路是这样的:

1.首先在百度上搜索对应的题号,然后采集百度上来自csdn的链接

2.对之前采集的链接利用类直接获取网页源码,从中解析code部分

3.最重要的一个剪枝,获取这个网页中<title>和</title>中的内容,从里面查找是否含有题号!!通过这个剪枝,整个AC率可以上升N倍~

4.认证code部分中是否含有"#include"这个内容,,这样可以防止code边框中可能是其他内容而不是代码

5.去杭电提交代码,如果通过了就下一题,不通过继续在之前解析的csdn中找代码

然后我们来一条一条的分析。。

首先放一个我自己写好的类,里面封装了最基础的http类和一些常用的函数(其实http类和Regex类只是把C#中的WebRequest类和Regex类简化了一下,,这样写起来更方便,然后稍微增加了几个常用到的文本处理的静态函数,以方便调用 )

[csharp] view
plain
 copy

print?

  1. /*Exyao_http.cs*/
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Net;
  7. using System.IO;
  8. using System.Diagnostics;
  9. using System.Text.RegularExpressions;
  10. namespace Exyao {
  11. class Exyao_http {
  12. public CookieContainer cookie = new CookieContainer();
  13. public string encode;
  14. public HttpWebResponse response;
  15. public Exyao_http(string _Encoding) {
  16. encode = _Encoding;
  17. }
  18. public string GET(string url) {
  19. HttpWebRequest http = (HttpWebRequest)WebRequest.Create(url);
  20. http.Method = "GET";
  21. http.CookieContainer = cookie;
  22. HttpWebResponse res = (HttpWebResponse)http.GetResponse();
  23. response = res;
  24. Stream s = res.GetResponseStream();
  25. StreamReader rs = new StreamReader(s, Encoding.GetEncoding(encode));
  26. string ret = rs.ReadToEnd();
  27. rs.Close();
  28. s.Close();
  29. return ret;
  30. }
  31. public string POST(string url, string data = "") {
  32. HttpWebRequest http = (HttpWebRequest)WebRequest.Create(url);
  33. http.CookieContainer = cookie;
  34. http.Method = "POST";
  35. http.ContentType = "application/x-www-form-urlencoded";
  36. Stream sd = http.GetRequestStream();
  37. StreamWriter sw = new StreamWriter(sd);
  38. sw.Write(data);
  39. sw.Close();
  40. HttpWebResponse res = (HttpWebResponse)http.GetResponse();
  41. response = res;
  42. Stream s = res.GetResponseStream();
  43. StreamReader rs = new StreamReader(s, Encoding.GetEncoding(encode));
  44. string ret = rs.ReadToEnd();
  45. rs.Close();
  46. s.Close();
  47. return ret;
  48. }
  49. public void deal_cookie() {
  50. string[] h = response.Headers.GetValues("Set-Cookie");
  51. if (h == null) return;
  52. foreach (string c in h) {
  53. cookie.SetCookies(response.ResponseUri, c);
  54. }
  55. }
  56. }
  57. class Exyao_Regex {
  58. private Regex r;
  59. private Match ret_;
  60. private bool is_all;
  61. private MatchCollection ret;
  62. public Exyao_Regex(string s) {
  63. r = new Regex(s);
  64. }
  65. public bool match(string s, bool all = false, int start = 0) {
  66. is_all = all;
  67. if (all) {
  68. ret = r.Matches(s, start);
  69. return ret.Count != 0;
  70. }
  71. else {
  72. ret_ = r.Match(s, start);
  73. return ret_.Success;
  74. }
  75. }
  76. public int getn() {
  77. return ret.Count;
  78. }
  79. public string get_text(int id = 0) {
  80. if (is_all) {
  81. return ret[id].Value;
  82. }
  83. else {
  84. return ret_.Value;
  85. }
  86. }
  87. public string get_subtext(int i = 0, int j = 0) {
  88. if (is_all) {
  89. GroupCollection g = ret[i].Groups;
  90. return g[j + 1].Value;
  91. }
  92. else {
  93. GroupCollection g = ret_.Groups;
  94. return g[j + 1].Value;
  95. }
  96. }
  97. }
  98. class F {
  99. public static string urlencode_utf8(string s) {
  100. byte[] bs = Encoding.GetEncoding("utf-8").GetBytes(s);
  101. int len = bs.Length;
  102. string ret = "";
  103. for (int i = 0; i < len; i++) {
  104. int c = bs[i];
  105. c = c < 0 ? 256 + c : c;
  106. if (c < 42 || c == 43 || c > 57 && c < 64 || c > 90 && c < 95 || c == 96 || c > 122) {
  107. ret += "%" + c.ToString("x2");
  108. }
  109. else {
  110. ret += (char)c;
  111. }
  112. }
  113. return ret;
  114. }
  115. public static string urlencode_gb2312(string url) {
  116. byte[] bs = Encoding.GetEncoding("gb2312").GetBytes(url);
  117. int len = bs.Length;
  118. string ret = "";
  119. for (int i = 0; i < len; i++) {
  120. int c = bs[i];
  121. c = c < 0 ? 256 + c : c;
  122. if (c < 42 || c == 43 || c > 57 && c < 64 || c > 90 && c < 95 || c == 96 || c > 122) {
  123. ret += "%" + c.ToString("x2");
  124. }
  125. else {
  126. ret += (char)c;
  127. }
  128. }
  129. return ret;
  130. }
  131. public static bool instr(string a, string b) {
  132. if (a.IndexOf(b) >= 0) return true;
  133. return false;
  134. }
  135. public static string substr(string s, string l, string r, int begin = 0) {
  136. int lp, rp;
  137. lp = s.IndexOf(l, begin);
  138. if (lp < 0) return "";
  139. rp = s.IndexOf(r, lp + l.Length);
  140. if (rp < 0) return "";
  141. return s.Substring(lp + l.Length, rp - lp - l.Length);
  142. }
  143. public static string readfile(string file, string encode = "gb2312") {
  144. FileStream fs = new FileStream(file, FileMode.Open);
  145. StreamReader sr = new StreamReader(fs, Encoding.GetEncoding(encode));
  146. string ret = sr.ReadToEnd();
  147. sr.Close();
  148. fs.Close();
  149. return ret;
  150. }
  151. public static void writefile(string file, string content = "", string encode = "gb2312") {
  152. FileStream fs = new FileStream(file, FileMode.OpenOrCreate);
  153. StreamWriter sw = new StreamWriter(fs, Encoding.GetEncoding(encode));
  154. sw.Write(content);
  155. sw.Close();
  156. fs.Close();
  157. }
  158. public static string gb2312_to_utf8(string s) {
  159. Encoding gb2312 = Encoding.GetEncoding("gb2312");
  160. Encoding utf8 = Encoding.GetEncoding("utf-8");
  161. byte[] w = gb2312.GetBytes(s);
  162. w = Encoding.Convert(gb2312, utf8, w);
  163. return utf8.GetString(w);
  164. }
  165. public static string utf8_to_gb2312(string s) {
  166. Encoding gb2312 = Encoding.GetEncoding("gb2312");
  167. Encoding utf8 = Encoding.GetEncoding("utf-8");
  168. byte[] w = utf8.GetBytes(s);
  169. w = Encoding.Convert(utf8, gb2312, w);
  170. return gb2312.GetString(w);
  171. }
  172. }
  173. }

我们先来尝试一下利用http协议直接获取百度的内容

用Google浏览器按F12点Network就可以开始录制封包

一般最重要的那一条里面都会带有很重要的参数,要么Method就是POST类型,要么就是GET中带上了很重要的参数

很明显,这里面第一条地址中带了我们搜索的内容,地址是

  1. http://www.baidu.com/s?wd=hdu1000&rsv_spt=1&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&tn=02049043_14_pg&rsv_enter=0&oq=hdu1000&rsv_t=f307M1pw6K7opc%2Fa5gyHuAJnjgYujheimT04UNMEmJrUz%2BBAo6%2B64AxFVh4WEuKUzFNKx2Q&rsv_pq=f83db5cf0000629e

我们可以稍微简化一下,其实可以就写成http://www.baidu.com/s?wd=hdu1000

这样我们就获得了地址,然后我们可以查看一下这个网页的源码

然后会得到html源码,找到大概的位置

一般html代码都是通过循环去生成的,所以每个块的代码基本都是类似的

我们只需要掌握这种代码格式就可以了

平时我们点击百度的地址就知道,,一般的链接刚开始使用的都是百度的短地址,然后会跳转,所以我们要采集那些短地址

把鼠标放到链接上,浏览器会显示出链接的地址,发现是以www.baidu.com/link开头的,于是我们在源码中搜索这一句

目标已经很明确了,就是要把他们取出来。。如果在acm里,,看似需要非常繁琐的文本处理,,

然而这是工程嘛,,用正则表达式效率已经高的惊人了。所以我们可以利用正则表达式写出匹配

因为我这下面这份函数是从以前写的,,当时就直接复制了,好像当时发现里面可能有可能引号是单引号的原因,所以写了两个正则去匹配

[csharp] view
plain
 copy

print?

  1. public static string[] baidu(string content) {
  2. Exyao_http http = new Exyao_http("utf-8");
  3. string html = http.GET("http://www.baidu.com/s?wd=" + F.urlencode_utf8(content) + "&ct=2097152&pn=0");
  4. Exyao_Regex reg = new Exyao_Regex("data-tools=\"\\{title:'(.*?)',url:'(.*?)'\\}\"");
  5. reg.match(html, true);
  6. List<string> ret = new List<string>();
  7. for (int i = 0; i < reg.getn(); i++) {
  8. ret.Add(reg.get_subtext(i, 1));
  9. }
  10. reg = new Exyao_Regex("data-tools='\\{\"title\":\"(.*?)\",\"url\":\"(.*?)\"\\}");
  11. reg.match(html, true);
  12. for (int i = 0; i < reg.getn(); i++) {
  13. ret.Add(reg.get_subtext(i, 1));
  14. }
  15. return ret.ToArray();
  16. }

另外讲一下几个关于网页表单很重要的编码。。。

一个是url编码,因为如果要传送的数据中包含一些符号,可能会对原本的文本的分隔符产生冲突,比如& / =等一些用于地址的符号,所以需要把这些符号转义

网页中都是利用url编码进行转义的,在之前发的类中我已封装好了函数。。

然后一个问题就是网页的编码,分gb2312和utf-8两种

gb2312是中文编码,utf-8是一种更普遍的面向更多语言的编码,两种编码在字母的ASCII码上,是一样的

但是在中文汉字上,gb2312是占2个字节,但是utf-8是占3个字节,这也会导致两种编码下的内容url编码之后会不一样

对于网页一般在源码中都会表明清楚采用的是什么源码。是什么源码你数据包就应该使用哪种源码

我的那个http类处理了一下网页编码,只要刚开始在初始化的时候填好编码,,之后返回出来的内容都会自动转码,但是提交表单的时候还是需要按照指定的编码去url编码

现在,,我们来考虑如何从csdn的博客中采集代码

就以hdu1000的第一个地址为基础,我们来查看它的网页源码

我们可以发现,,标题肯定是在<title>里面啊,,我们需要先采集一次标题,,然后看我们的题号是否在这个标题中。如果不在,那这篇文章肯定就不是正确代码啊,,

acmer写题解当然会把题号写在标题中~~

再看代码可以发现,代码框里面的内容都在<pre>这个标签里面,,所以我们就可以采集整个网页中出现的<pre>标签里面的内容

然后在这个内容里面寻找是否有"#include",如果有,说明这个里面的代码就可能是答案了咯

[csharp] view
plain
 copy

print?

  1. public static string getcode(string url, string id = "") {
  2. Exyao_http http = new Exyao_http("utf-8");//csdn都是utf-8编码
  3. Exyao_Regex zz = new Exyao_Regex("<pre.*?>([\\s\\S]*?)<\\/pre>");//正则表达式匹配所有的pre标签
  4. string html = http.GET(url);
  5. url = http.response.ResponseUri.ToString();
  6. if (!F.intext(url, "/article/details/")) return "";//如果网页的地址没有包括这个,说明不是博客的文章页面
  7. string title = F.substr(html, "<title>", "</title>");//获取标题
  8. if (!F.intext(title, id)) return "";//如果标题中没有题号
  9. if (!F.intext(title, "hdu") && !F.intext(title, "HDU")) return "";//防止标题是其他oj的对应题号
  10. zz.match(html, true);//正则匹配全部的pre标签
  11. for (int i = 0; i < zz.getn(); i++) {
  12. string code = zz.get_subtext(i, 0);
  13. if (!F.intext(code, "#include")) continue;
  14. code = code.Replace("<", "<");//下面全部都是转义一些符号
  15. code = code.Replace(">", ">");
  16. code = code.Replace(""", "\"");
  17. code = code.Replace("&", "&");
  18. code = code.Replace(" ", " ");
  19. Exyao_Regex reg = new Exyao_Regex("&#(.*?);");//这个也是为了转义符号
  20. reg.match(code, true);
  21. for (int j = 0; j < reg.getn(); j++) {
  22. string p = "";
  23. p += (char)(int.Parse(reg.get_subtext(j, 0)));
  24. code = code.Replace(reg.get_text(j), p);
  25. }
  26. //有的acmer喜欢在代码前面说一些感慨,所以代码要从第一个#include的位置开始,这样可以防止一些RE
  27. return code.Substring(code.IndexOf("#include"));
  28. }
  29. return "";//找不到code
  30. }

剩下的,,我们需要封装一下关于杭电oj的操作,估计很多人都是冲着如何POST而过来看的把。。

利用我们之前教的截取封包,我们来截取一下hdu的登录封包

这条的Method是POST方法,,因为截图空间有限没截取出来,,我们很容易就能发现数据包

于是可以得到POST的地址和数据

  1. 地址 http://acm.hdu.edu.cn/userloginex.php?action=login
  2. 数据 username=账号&userpass=密码&login=Sign+In

提交数据部分,也是一样的道理,都可以利用抓包工具获得

下面是关于杭电登录和提交的代码

之所以不发文件,和上面代码不直接发代码是为了防止恶意刷,,毕竟这个只是无聊做的,并不想被当成恶意攻击杭电oj,,(毕竟我还是很喜欢hduoj的)

之前我提交也都是选在凌晨,并且每份代码中间加了10s的延时,也是为了防止影响到别人刷题..

关于http协议方面还能做出许多很好玩的东西,,基本上网页能达到的效果,都可以通过程序直接模拟封包来完成

上次听别人说我博客好像不能留言了。。(不知道是不是真的→_→),,求测试。

C#利用POST实现杭电oj的AC自动机器人,AC率高达50%~~的更多相关文章

  1. 杭电oj 2037 今年暑假不AC

    Tips:贪心算法的典型应用,可以按照节目结束时间由小到大排序,(至于结束时间相同的,有些人说按开始时间早的排序,不过个人认为不必处理,因为结束时间一样,两个之中要么都没有,要么必有一个)然后再依次进 ...

  2. 杭电oj 4004---The Frog Games java解法

    import java.util.Arrays; import java.util.Scanner; //杭电oj 4004 //解题思路:利用二分法查找,即先选取跳跃距离的区间,从最大到最小, // ...

  3. 杭电oj 2095 & 异或^符号在C/C++中的使用

    异或^符号,在平时的学习时可能遇到的不多,不过有时使用得当可以发挥意想不到的结果. 值得注意的是,异或运算是建立在二进制基础上的,所有运算过程都是按位异或(即相同为0,不同为1,也称模二加),得到最终 ...

  4. 用python爬取杭电oj的数据

    暑假集训主要是在杭电oj上面刷题,白天与算法作斗争,晚上望干点自己喜欢的事情! 首先,确定要爬取哪些数据: 如上图所示,题目ID,名称,accepted,submissions,都很有用. 查看源代码 ...

  5. 『ACM C++』HDU杭电OJ | 1415 - Jugs (灌水定理引申)

    今天总算开学了,当了班长就是麻烦,明明自己没买书却要带着一波人去领书,那能怎么办呢,只能说我善人心肠哈哈哈,不过我脑子里突然浮起一个念头,大二还要不要继续当这个班委呢,既然已经体验过就可以适当放下了吧 ...

  6. 杭电oj————2057(java)

    question:A+ B again 思路:额,没啥思路/捂脸,用java的long包里的方法,很简单,只是有几次WA,有几点要注意一下 注意:如果数字有加号要删除掉,这里用到了正则表达式“\\+” ...

  7. 爬取杭电oj所有题目

    杭电oj并没有反爬 所以直接爬就好了 直接贴源码(参数可改,循环次数可改,存储路径可改) import requests from bs4 import BeautifulSoup import ti ...

  8. 杭电 OJ 提交代码需要注意的问题

    杭电acm 提交代码需要注意的问题 1. 用 Java 的时候类名请用 Main 2. Java 提交出现 PE 的可能原因有 1) 最基本的错误是空格问题,比如注意每行的末尾是否输出空格 2) 用 ...

  9. 『ACM C++』HDU杭电OJ | 1418 - 抱歉 (拓扑学:多面体欧拉定理引申)

    呕,大一下学期的第一周结束啦,一周过的挺快也挺多出乎意料的事情的~ 随之而来各种各样的任务也来了,嘛毕竟是大学嘛,有点上进心的人多多少少都会接到不少任务的,忙也正常啦~端正心态 开心面对就好啦~ 今天 ...

随机推荐

  1. Save ITCM

    Debug String if below 64 BYTE it will in DRAM or it will in ITCM So to save ITCM , move it to ALLStr ...

  2. c# Task编程一个task抛出异常后怎么取消其他线程

    从MSDN的Forum上看到别人提供的解决方案,感觉还是比较靠谱,所以就保存下来. CancellationTokenSource cts = new CancellationTokenSource( ...

  3. C++Primer STL算法

    //1.概览: // A:beg和end是表示元素范围的迭代器. // B:beg2是表示第二个输入序列开始位置的迭代器.end2表示第二个序列的末尾位置,若没有end2,则假定beg2表示的序列至少 ...

  4. F面经prepare:strstr变种

    * Given an integer k>=1 and two strings A and B (length ~n each); * Figure out if there is any co ...

  5. F面经:painting house

    There are a row of houses, each house can be painted with three colors red, blue and green. The cost ...

  6. csuoj 1396: Erase Securely

    http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1396 1396: Erase Securely Time Limit: 1 Sec  Memory ...

  7. const 关键字及作用

    1.const 修饰一般常量,可以把变量变成常量 例如: int  num=10; num=100; printf(“num=%d\n”,num); 输出的来得值为:100: 但是如果const in ...

  8. fences(桌面整理软件)与eDiary3.3.3下载链接

    fences:  http://www.jb51.net/softs/309746.html http://jingyan.baidu.com/article/e8cdb32b6e958337042b ...

  9. Java基础(4):Scanner输入的典型应用

    import java.util.Scanner; /* * 功能:为指定的成绩加分,直到分数大于等于60为止 * 输出加分前的成绩和加分后的成绩,并且统计加分的次数 * 步骤: * 1.定义一个变量 ...

  10. C#: log4net

    log4net.dll是apache发布的用来记录log的dll文件 这里举个例子相信大家就知道怎么用了,新建一个console项目,添加log4net.dll后再添加应用程序配置文件 <?xm ...