使用TaskManager爬取2萬條代理IP實現自動投票功能

焰尾迭發表於2015-09-20

話說某天心血來潮想到一個問題,朋友圈裡面經常有人發投票連結,讓幫忙給XX投票,以前呢會很自覺開啟連結幫忙投一票。可是這種事做多了就會考慮能不能使用工具來進行投票呢,身為一名程式猿決定研究解決這個問題。於是有了以下思考

1.是否能一個人投多票,如果不行又是什麼限制了一人投多票?

答:投票網站限制了一個IP或者一個使用者只能投一票,防止惡意刷票行為

2.如果是一個IP一票那是否代表著多個IP就能投多票了呢?

答:答案是肯定的

3.用什麼方法能夠在程式碼裡面改變自己請求的IP?

答:HTTP請求的時候設定代理IP

4.多個代理IP從哪裡獲取,獲取到之後我又該如何使用程式碼自動化投票?

答:請看文章後面內容

本篇將介紹TaskManager內建任務-代理IP爬蟲實現細節,你需要準備的知識:HtmlAgilityPack解析HTML,Quart.net

代理IP介紹

百度百科介紹:代理(英語:Proxy),也稱網路代理,是一種特殊的網路服務,允許一個網路終端(一般為客戶端)通過這個服務與另一個網路終端(一般為伺服器)進行非直接的連線。一些閘道器、路由器等網路裝置具備網路代理功能。一般認為代理服務有利於保障網路終端的隱私或安全,防止攻擊。

目前有很多廠商提供代理IP線上獲取,但是很多都是提供幾十個試用的,如果想使用更多的代理IP,則需付費購買。這裡我找到了一個提供很多代理IP的網站,可以自行百度“代理ip”(以免認為我打廣告),或者參考開源TaskManager介紹這篇文章。

有了這麼多線上的代理IP可以解決文章開頭的問題4了,可是還有個問題這些資料都是網頁上的,我在程式碼裡面怎麼使用呢?這就用到了HtmlAgilityPack工具包,看名稱就能猜到是用來解析HTML的。

HtmlAgilityPack使用

HtmlAgilityPack是一個開源的解析HTML元素的類庫,最大的特點是可以通過XPath來解析HMTL,如果您以前用C#操作過XML,那麼使用起HtmlAgilityPack也會得心應手。

解析簡單的HTML

string HTML = @"<html><head><title>簡單解析測試</title></head><body>
                    <div id='div1' title='div1'>
                        <table>
                             <tr>
                                <td>1</td>
                                <td title='cn'>cn</td>
                            </tr>
                        </table>
                    </div>
                </body></html>";
            var doc = new HtmlDocument();
            doc.LoadHtml(HTML);
            //輸出頁面標題
            Console.WriteLine("頁面title:"+doc.DocumentNode.SelectSingleNode("/html/head/title").InnerText);
            //獲取div1節點  方式1
            HtmlNode divNode1 = doc.GetElementbyId("div1");
            //獲取div1節點  方式2
            HtmlNode divNode2 = doc.DocumentNode.SelectSingleNode("//div[@id='div1']");
            //判斷節點1和節點2是否相同
            Console.WriteLine("斷節點1和節點2是否相同:" + (divNode1 == divNode2));
            //獲取頁面所有table
            HtmlNodeCollection tableCollection = doc.DocumentNode.SelectNodes("//table");
            Console.WriteLine("頁面table數量:"+tableCollection.Count);
            //獲取table下所有td並輸出資訊
            HtmlNodeCollection tdCollection = tableCollection[0].SelectNodes("tr/td");
            foreach (var td in tdCollection)
            {
                HtmlAttribute atr = td.Attributes["title"];
                Console.WriteLine("td InnerText:" + td.InnerText + " | td title屬性值:" + (atr == null ? "" : atr.Value));
            }
            Console.Read();

代理IP爬蟲實現

會了HtmlAgilityPack的一些簡單操作之後進入正式爬取過程,由於需要爬取的網頁帶IP封鎖功能(一段時間請求頻率過高封鎖當前IP),在設計過程中我採用了爬取五次自動換代理IP突破網站限制(感覺自己壞壞的)。

整體實現邏輯

在.net裡面使用WebRequest可以模擬HTTP的get Post請求,最終要的一點能設定請求時使用的代理IP,重點關注我標紅的程式碼

/// <summary>
        /// 代理使用示例
        /// </summary>
        /// <param name="Url"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string GetUrltoHtml(string Url, string type)
        {
            try
            {
                System.Net.WebRequest wReq = System.Net.WebRequest.Create(Url);

                WebProxy myProxy = new WebProxy("192.168.15.11", 8015);
                //建議連線(代理需要身份認證,才需要使用者名稱密碼)
                myProxy.Credentials = new NetworkCredential("admin", "123456");
                //設定請求使用代理資訊
                wReq.Proxy = myProxy;
                // Get the response instance.
                System.Net.WebResponse wResp = wReq.GetResponse();
                System.IO.Stream respStream = wResp.GetResponseStream();
                // Dim reader As StreamReader = New StreamReader(respStream)
                using (System.IO.StreamReader reader = new System.IO.StreamReader(respStream, Encoding.GetEncoding(type)))
                {
                    return reader.ReadToEnd();
                }
            }
            catch (System.Exception ex)
            {
                //errorMsg = ex.Message;
            }
            return "";
        }

瞭解如何使用代理IP,離我們的目標又近了一步,下面就是代理IP獲取的實現了,由於程式碼有點多,我這裡只貼出重要部分,IpProxyGet.cs原始碼可到文章末尾自行下載。

        /// <summary>
        /// 獲取總頁數
        /// </summary>
        /// <returns>總頁數</returns>
        private static int GetTotalPage(string IPURL, string ProxyIp)
        {
            var doc = new HtmlDocument();
            doc.LoadHtml(GetHTML(IPURL, ProxyIp));
            var res = doc.DocumentNode.SelectNodes(@"//div[@class='pagination']/a");
            if (res != null && res.Count > 2)
            {
                int page;
                if (int.TryParse(res[res.Count - 2].InnerText, out page))
                {
                    return page;
                }
            }
            return 1;
        }

解析每一頁HTML資料

 /// <summary>
        /// 解析每一頁資料
        /// </summary>
        /// <param name="param"></param>
        private static void DoWork(object param)
        {
            //引數還原
            Hashtable table = param as Hashtable;
            int start = Convert.ToInt32(table["start"]);
            int end = Convert.ToInt32(table["end"]);
            List<IPProxy> list = table["list"] as List<IPProxy>;
            ProxyParam Param = table["param"] as ProxyParam;

            //頁面地址
            string url = string.Empty;
            string ip = string.Empty;
            IPProxy item = null;
            HtmlNodeCollection nodes = null;
            HtmlNode node = null;
            HtmlAttribute atr = null;
            for (int i = start; i <= end; i++)
            {
                LogHelper.WriteLog(string.Format("開始解析,頁碼{0}~{1},當前頁碼{2}", start, end, i));
                url = string.Format("{0}/{1}", Param.IPUrl, i);
                var doc = new HtmlDocument();
                doc.LoadHtml(GetHTML(url, Param.ProxyIp));
                //獲取所有資料節點tr
                var trs = doc.DocumentNode.SelectNodes(@"//table[@id='ip_list']/tr");
                if (trs != null && trs.Count > 1)
                {
                    LogHelper.WriteLog(string.Format("當前頁碼{0},請求地址{1},共{2}條資料", i, url, trs.Count));
                    for (int j = 1; j < trs.Count; j++)
                    {
                        nodes = trs[j].SelectNodes("td");
                        if (nodes != null && nodes.Count > 9)
                        {
                            ip = nodes[2].InnerText.Trim();
                            if (Param.IsPingIp && !Ping(ip))
                            {
                                continue;
                            }
                            //有效的IP才新增
                            item = new IPProxy();

                            node = nodes[1].FirstChild;
                            if (node != null)
                            {
                                atr = node.Attributes["alt"];
                                if (atr != null)
                                {
                                    item.Country = atr.Value.Trim();
                                }
                            }

                            item.IP = ip;
                            item.Port = nodes[3].InnerText.Trim();
                            item.ProxyIp = GetIP(item.IP, item.Port);
                            item.Position = nodes[4].InnerText.Trim();
                            item.Anonymity = nodes[5].InnerText.Trim();
                            item.Type = nodes[6].InnerText.Trim();

                            node = nodes[7].SelectSingleNode("div[@class='bar']");
                            if (node != null)
                            {
                                atr = node.Attributes["title"];
                                if (atr != null)
                                {
                                    item.Speed = atr.Value.Trim();
                                }
                            }

                            node = nodes[8].SelectSingleNode("div[@class='bar']");
                            if (node != null)
                            {
                                atr = node.Attributes["title"];
                                if (atr != null)
                                {
                                    item.ConnectTime = atr.Value.Trim();
                                }
                            }
                            item.VerifyTime = nodes[9].InnerText.Trim();
                            list.Add(item);
                        }
                    }
                    LogHelper.WriteLog(string.Format("當前頁碼{0},共{1}條資料", i, trs.Count));
                }
                LogHelper.WriteLog(string.Format("結束解析,頁碼{0}~{1},當前頁碼{2}", start, end, i));
            }
        }

最終會獲取2萬多條資料

自動投票簡單實現

這裡使用.net的WebBrowser控制元件來載入頁面,最終效果如下

 #region 設定代理IP
        private void button2_Click(object sender, EventArgs e)
        {
            string proxy = this.textBox1.Text;
            RefreshIESettings(proxy);
            IEProxy ie = new IEProxy(proxy);
            ie.RefreshIESettings();
            //MessageBox.Show(ie.RefreshIESettings().ToString());
        }
        #endregion
        #region 取消代理IP
        private void button3_Click(object sender, EventArgs e)
        {
            IEProxy ie = new IEProxy(null);
            ie.DisableIEProxy();
        }
        #endregion
        #region 開啟網頁
        private void button1_Click(object sender, EventArgs e)
        {
            string url = txt_url.Text.Trim();
            if (string.IsNullOrEmpty(url))
            {
                MessageBox.Show("請輸入要開啟的網址");
                return;
            }
            this.webBrowser1.Navigate(url, null, null, null);
        }
        #endregion

總結

本篇要介紹的內容到此結束了,下面寫點我的期待!希望有喜歡的朋友一起來完善TaskManager(完全開源的),使之成為一款能夠提高生活便捷性的工具,新增很多新任務。比如:第二天要下雨或者下雪,發個郵件提醒,帶上雨傘…。好了到了放出原始碼的時間了。敬請期待下一篇!

簡單投票原始碼:http://files.cnblogs.com/files/yanweidie/SimpleIP.rar

TaskManagerSVN地址:http://code.taobao.org/svn/TaskManagerPub/Branch   使用svn checkout指令進行下載。

GitHub地址:https://github.com/CrazyJson/TaskManager

體驗工具下載地址:TaskManager  解壓後檔案執行合併SQL,修改Config.xml資料庫連線,使用WSWinForm進行安裝。

相關文章