c#批量抓取免费代理并验证有效性

之前看到某公司的官网的文章的浏览量刷新一次网页就会增加一次,给人的感觉不太好,一个公司的官网给人如此直白的漏洞,我批量发起请求的时候发现页面打开都报错,100多人的公司的官网文章刷新一次你给我看这个,这公司以前来过我们学校宣传招人+在园子里搜招聘的时候发现居然以前招xamarin,挺好奇的,所以就关注过。好吧不说这些了,只是扯扯蛋而已,回归主题,我想说的是csdn的文章可以通过设置代理ip刷新文章的浏览量,所以首先要做的就是这篇文章的主题“使用c#验证代理ip有效性”。

当然代理IP来源肯定是免费,所以嘛效率一般,从一些免费的代理ip的网页抓取的代理IP并不一定都是有用的,所以需要我们对我们抓取的代理ip进行验证,代理ip的有效时间也是有限,从10几秒到1个小时不限,大多数时间非常短,所以比如说,我们1分钟需要100个代理ip,那就1分钟获取一次,每次获取100个(这里是理想状态下的,抓取的代理ip都是有效的),原则上来说抓取下来后应该立即马上被使用。

当然这篇文章比较基础,一直觉得爬虫比较有趣,其实我在爬虫方面也是个小白,只是做一个简单的记录,如果有什么错误的地方,希望能提出建议。针对下面几个问题,我们就可以完成如何验证代理IP有效性的检测了。

1.从哪些网页上可以抓取免费的代理IP?

http://www.xicidaili.com

http://www.ip3366.net

http://www.66ip.cn

百度一下“免费代理ip”挺多的。

2.代理IP稳定吗?有什么作用?

这种免费的代理ip时效性和有效性都不强,上面这三个免费的代理网站,时效性大概在十几秒到1个小时不等,一般需要自己处理验证后使用,提高命中率。可适用于隐藏网页IP(有些网站还不准使用代理ip,比如豆瓣,其实挺尴尬的,内容这么贵吗),一般常用于空间留言、刷网站流量、网赚任务、批量注册账号等,只要没有其他限制,需要频繁更换ip都可以使用。

3.ping通IP就是有效的吗?如何验证代理是否有效

好吧,这有点废话,进行端口测试才是最有效的,能ping通并不代表代理有效,不能平通也不一定代理不可用。可以使用HttpWebRequest,也可以使用Scoket,当然HttpWebRequest比Socket连接代理ip、port要慢。

4.一次提取多少代理合适?

代理ip时效性不强、并且有效性也不高,所以只能从一些代理ip的网站上批量定时去获取,有的代理在一分钟内使用是有限制的,所以说限制比较多。

5.http代理和https代理有什么区别?

需要访问https的网站就需要使用https代理了,比如百度,需要访问http的代理,可以使用http。这个并不是100%的。

检测代理ip有效性步骤如下:

1.使用HttpWebRequest、HttpWebResponse请求代理ip的网页,获取包含代理的网页内容

2.使用HtmlAgilityPack或者正则表达式对抓取的内容进行截取,保存到代理集合

3.拿到代理集合,多线程发起http请求,比如访问百度,是否成功,成功则存到Redis里面。

效果图如下:

  • 使用HttpWebRequest发起请求

Request.cs如下,主要就是两个方法,一个方法是验证代理ip是否有效,设置HttpWebRequest的Proxy属性,请求百度,看到有些文章大多数会获取响应的内容,如果内容符合请求的网址则证明代理哟有效,实际上根据HttpStatusCode 200就可以判断是否验证有效。

【注意】建的是控制台程序,使用了异步,所以还是建.net core吧,c#语言的版本7.1。C#如何在控制台程序中使用异步

复制代码
 1  public class Request
 2     {
 3         /// <summary>
 4         /// 验证代理ip有效性
 5         /// </summary>
 6         /// <param name="proxyIp">代理IP</param>
 7         /// <param name="proxyPort">代理IP 端口</param>
 8         /// <param name="timeout">详情超时</param>
 9         /// <param name="url">请求的地址</param>
10         /// <param name="success">成功的回调</param>
11         /// <param name="fail">失败的回调</param>
12         /// <returns></returns>
13         public static async System.Threading.Tasks.Task getAsync(string proxyIp,int  proxyPort, int  timeout,string url, Action success, Action<string> fail)
14         {
15             System.GC.Collect();
16             HttpWebRequest request = null;
17             HttpWebResponse response = null;
18             try
19             {
20                 request = (HttpWebRequest)WebRequest.Create(url);
21                 //HttpWebRequest request = HttpWebRequest.CreateHttp(url);
22                 request.Timeout =timeout;
23                 request.KeepAlive = false;
24                 request.Proxy = new WebProxy(proxyIp,proxyPort);
25                 response =  await  request.GetResponseAsync() as HttpWebResponse;
26                 if (response.StatusCode == HttpStatusCode.OK)
27                 {
28                     success();
29                 }
30                 else
31                 {
32                     fail(response.StatusCode+":"+response.StatusDescription);
33                 }
34             }
35             catch (Exception ex)
36             {
37                 fail("请求异常"+ex.Message.ToString());
38             }
39             finally
40             {
41                 if (request != null)
42                 {
43                     request.Abort();
44                     request = null;
45                 }
46                 if (response != null)
47                 {
48                     response.Close();
49                 }
50             }
51         }
52 
53         /// <summary>
54         ///  发起http请求
55         /// </summary>
56         /// <param name="url"></param>
57         /// <param name="success">成功的回调</param>
58         /// <param name="fail">失败的回调</param>
59         public static void get(string url,Action<string> success,Action<string> fail)
60         {
61             StreamReader reader = null;
62             Stream stream = null;
63             WebRequest request = null;
64             HttpWebResponse response = null;
65             try
66             {
67                 request = WebRequest.Create(url);
68                 request.Timeout = 2000;
69                 response = (HttpWebResponse)request.GetResponse();
70                 if (response.StatusCode == HttpStatusCode.OK)
71                 {
72                     stream = response.GetResponseStream();
73                     reader = new StreamReader(stream);
74                     string result = reader.ReadToEnd();
75                     success(result);
76                 }
77                 else
78                 {
79                     fail(response.StatusCode+":"+response.StatusDescription);
80                 }
81             }
82             catch (Exception ex)
83             {
84                 fail(ex.ToString());
85             }
86             finally
87             {
88                 if (reader != null)
89                     reader.Close();
90                 if (stream != null)
91                     stream.Close();
92                 if(response!=null)
93                     response.Close();
94                 if(request!=null)
95                     request.Abort();
96             }
97         }
98     }
复制代码
  • 抓取免费代理,并检查是否有效

ProxyIpHelper.cs 中主要有四个方法,检查ip是否可用CheckProxyIpAsync、抓取xicidaili.com的代理GetXicidailiProxy、抓取ip3366.net的代理GetIp3366Proxy、抓取66ip.cn的代理GetIp3366Proxy。如果想多抓取几个网站可以多写几个。

复制代码
 public class ProxyIpHelper
    {
        private static string address_xicidaili = "http://www.xicidaili.com/wn/{0}";
        private static string address_66ip = "http://www.66ip.cn/nmtq.php?getnum=20&isp=0&anonymoustype=0&start=&ports=&export=&ipaddress=&area=1&proxytype=1&api=66ip";
        private static string address_ip3366 = "http://www.ip3366.net/?stype=1&page={0}";
        /// <summary>
        /// 检查代理IP是否可用
        /// </summary>
        /// <param name="ipAddress">ip</param>
        /// <param name="success">成功的回调</param>
        /// <param name="fail">失败的回调</param>
        /// <returns></returns>
        public  static async Task CheckProxyIpAsync(string ipAddress, Action success, Action<string> fail)
        {
            int index = ipAddress.IndexOf(":");
            string proxyIp = ipAddress.Substring(0, index);
            int proxyPort = int.Parse(ipAddress.Substring(index + 1));
            await Request.getAsync(proxyIp, proxyPort, 3000, "https://www.baidu.com/", () =>
            {
                success();
            }, (error) =>
            {
                fail(error);
            });
        }
        /// <summary>
        /// 从xicidaili.com网页上去获取代理IP,可以分页
        /// </summary>
        /// <param name="page"></param>
        /// <returns></returns>
        public static List<string> GetXicidailiProxy(int page)
        {
            List<string> list = new List<string>();
            for (int p = 1; p <= page; p++)
            {
                string url = string.Format(address_xicidaili, p);
               Request.get(url,(docText)=> {
                   if (!string.IsNullOrWhiteSpace(docText))
                   {
                       HtmlDocument doc = new HtmlDocument();
                       doc.LoadHtml(docText);
                       var trNodes = doc.DocumentNode.SelectNodes("//table[@id='ip_list']")[0].SelectNodes("./tr");
                       if (trNodes != null && trNodes.Count > 0)
                       {
                           for (int i = 1; i < trNodes.Count; i++)
                           {
                               var tds = trNodes[i].SelectNodes("./td");
                               string ipAddress = tds[1].InnerText + ":" + int.Parse(tds[2].InnerText); ;
                               list.Add(ipAddress);
                           }
                       }
                   }
               },(error)=> {
                   Console.WriteLine(error);
               });
            }
            return list;  
         }
        /// <summary>
        /// 从ip3366.net网页上去获取代理IP,可以分页
        /// </summary>
        /// <param name="page"></param>
        /// <returns></returns>
        public static List<string> GetIp3366Proxy(int page)
        {
            List<string> list = new List<string>();
            for (int p = 1; p <= page; p++)
            {
                string url = string.Format(address_ip3366, p);
                Request.get(url, (docText) => {
                    if (!string.IsNullOrWhiteSpace(docText))
                    {
                        HtmlDocument doc = new HtmlDocument();
                        doc.LoadHtml(docText);
                        var trNodes1 = doc.DocumentNode.SelectNodes("//table")[0];
                        var trNodes2 = doc.DocumentNode.SelectNodes("//table")[0].SelectSingleNode("//tbody");
                        var trNodes = doc.DocumentNode.SelectNodes("//table")[0].SelectSingleNode("//tbody").SelectNodes("./tr");
                        if (trNodes != null && trNodes.Count > 0)
                        {
                            for (int i = 1; i < trNodes.Count; i++)
                            {
                                var tds = trNodes[i].SelectNodes("./td");
                                if (tds[3].InnerHtml == "HTTPS")
                                {
                                    string ipAddress = tds[0].InnerText + ":" + int.Parse(tds[1].InnerText); ;
                                    list.Add(ipAddress);
                                }
                            }
                        }
                    }
                }, (error) => {
                    Console.WriteLine(error);
                });
            }
            return list;
         }
        /// <summary>
        /// 从66ip.cn中去获取,不需要分页
        /// </summary>
        /// <returns></returns>
        public static List<string> Get66ipProxy()
        {
            List<string> list = new List<string>();
            Request.get(address_66ip,
            (docText)=> {
                int count = 0;
                if (string.IsNullOrWhiteSpace(docText) == false)
                {
                    string regex = "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}";
                    Match mstr = Regex.Match(docText, regex);
                    while (mstr.Success && count < 20)
                    {
                        string tempIp = mstr.Groups[0].Value;
                        list.Add(tempIp);
                        mstr = mstr.NextMatch();
                        count++;
                    }
                }
            },
            (error)=> {
                Console.WriteLine(error);
            });
            return list;
        }
    }
复制代码
  • 使用Timer定时抓取,并检查,成功则保存到redis

c#有三种定时器,这里定时器是使用System.Threading命名空间, 这个Timer会开启新的线程,抓取三个网页定义了三个Timer对象。每一次抓取都会保存上一次抓取的集合,检查前,会进行对比,取出新的集合也就是没有重复的那部分。有效性的ip比较低,这里没有做统计,如果代码再优化一下,可以做一下统计,看看程序的主入口吧,最终的实现如下:

View Code

Redis第三库使用的stackoverflow的 StackExchange.Redis,代理ip不能重复储存,所以采用的数据结构是Set。存的值非常简单就一个ip加上port,也可以存入更多相关信息,感觉没必要。即使有这些其他的信息,也很难发挥作用。RedisHelper.cs如下

View Code

总结

明天补上刷新网页浏览量的文章吧,代码还不够好,ip的有效性还不高,对多线程的使用还不是很熟练

--------

7月6号的补充:完成csdn刷文章的浏览量了:C#使用代理Ip刷新csdn文章浏览量

原文地址:https://www.cnblogs.com/yidanda888/p/12768485.html