• C#利用POST实现杭电oj的AC自动机器人,AC率高达50%~~


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

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


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

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



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

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

    [html] view plain copy
     print?
    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.   
    5.             Exyao_Regex reg = new Exyao_Regex("data-tools="\{title:'(.*?)',url:'(.*?)'\}"");  
    6.             reg.match(html, true);  
    7.   
    8.             List<string> ret = new List<string>();  
    9.             for (int i = 0; i < reg.getn(); i++) {  
    10.                 ret.Add(reg.get_subtext(i, 1));  
    11.             }  
    12.   
    13.             reg = new Exyao_Regex("data-tools='\{"title":"(.*?)","url":"(.*?)"\}");  
    14.             reg.match(html, true);  
    15.   
    16.             for (int i = 0; i < reg.getn(); i++) {  
    17.                 ret.Add(reg.get_subtext(i, 1));  
    18.             }  
    19.             return ret.ToArray();  
    20.         }  

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

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

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

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



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

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

    [plain] view plain copy
     print?
    1. 地址 http://acm.hdu.edu.cn/userloginex.php?action=login  
    2. 数据 username=账号&userpass=密码&login=Sign+In  
    提交数据部分,也是一样的道理,都可以利用抓包工具获得

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


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

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


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


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

  • 相关阅读:
    分析模式的位置
    SAP .Net Connector for C#
    NETBPM开源工作流讲座
    BW处理链的时间为什么会每天推迟2秒钟?
    如何在SubScreen中取得上一screen中的值
    flash弹出窗口被ie屏蔽的解决方法
    用Eclipse 开发Flex (配置安装Flex插件)
    rtmp和http方式在播放flv方面的各自优势和劣势
    FMS4 P2P直播解决方案
    [AS3]URLLoader+URLRequest+JPGEncoder实现BitmapData图片数据保存
  • 原文地址:https://www.cnblogs.com/im0qianqian/p/5989364.html
Copyright © 2020-2023  润新知