前言
之前看到某公司的官網(wǎng)的文章的瀏覽量刷新一次網(wǎng)頁就會增加一次,給人的感覺不太好,一個公司的官網(wǎng)給人如此直白的漏洞,我批量發(fā)起請求的時候發(fā)現(xiàn)頁面打開都報錯,100多人的公司的官網(wǎng)文章刷新一次你給我看這個,這公司以前來過我們學(xué)校宣傳招人+在園子里搜招聘的時候發(fā)現(xiàn)居然以前招xamarin,挺好奇的,所以就關(guān)注過。好吧不說這些了,只是扯扯蛋而已,回歸主題,我想說的是csdn的文章可以通過設(shè)置代理ip刷新文章的瀏覽量,所以首先要做的就是這篇文章的主題“使用c#驗證代理ip有效性”。
當(dāng)然代理ip來源肯定是免費(fèi),所以嘛效率一般,從一些免費(fèi)的代理ip的網(wǎng)頁抓取的代理ip并不一定都是有用的,所以需要我們對我們抓取的代理ip進(jìn)行驗證,代理ip的有效時間也是有限,從10幾秒到1個小時不限,大多數(shù)時間非常短,所以比如說,我們1分鐘需要100個代理ip,那就1分鐘獲取一次,每次獲取100個(這里是理想狀態(tài)下的,抓取的代理ip都是有效的),原則上來說抓取下來后應(yīng)該立即馬上被使用。
當(dāng)然這篇文章比較基礎(chǔ),一直覺得爬蟲比較有趣,其實我在爬蟲方面也是個小白,只是做一個簡單的記錄,如果有什么錯誤的地方,希望能提出建議。針對下面幾個問題,我們就可以完成如何驗證代理ip有效性的檢測了。
1.從哪些網(wǎng)頁上可以抓取免費(fèi)的代理ip?
http://www.xicidaili.com
http://www.ip3366.net
http://www.66ip.cn
百度一下“免費(fèi)代理ip”挺多的。
2.代理ip穩(wěn)定嗎?有什么作用?
這種免費(fèi)的代理ip時效性和有效性都不強(qiáng),上面這三個免費(fèi)的代理網(wǎng)站,時效性大概在十幾秒到1個小時不等,一般需要自己處理驗證后使用,提高命中率。可適用于隱藏網(wǎng)頁ip(有些網(wǎng)站還不準(zhǔn)使用代理ip,比如豆瓣,其實挺尷尬的,內(nèi)容這么貴嗎),一般常用于空間留言、刷網(wǎng)站流量、網(wǎng)賺任務(wù)、批量注冊賬號等,只要沒有其他限制,需要頻繁更換ip都可以使用。
3.ping通ip就是有效的嗎?如何驗證代理是否有效
好吧,這有點廢話,進(jìn)行端口測試才是最有效的,能ping通并不代表代理有效,不能平通也不一定代理不可用。可以使用httpwebrequest,也可以使用scoket,當(dāng)然httpwebrequest比socket連接代理ip、port要慢。
4.一次提取多少代理合適?
代理ip時效性不強(qiáng)、并且有效性也不高,所以只能從一些代理ip的網(wǎng)站上批量定時去獲取,有的代理在一分鐘內(nèi)使用是有限制的,所以說限制比較多。
5.http代理和https代理有什么區(qū)別?
需要訪問https的網(wǎng)站就需要使用https代理了,比如百度,需要訪問http的代理,可以使用http。這個并不是100%的。
檢測代理ip有效性步驟如下:
1.使用httpwebrequest、httpwebresponse請求代理ip的網(wǎng)頁,獲取包含代理的網(wǎng)頁內(nèi)容
2.使用htmlagilitypack或者正則表達(dá)式對抓取的內(nèi)容進(jìn)行截取,保存到代理集合
3.拿到代理集合,多線程發(fā)起http請求,比如訪問百度,是否成功,成功則存到redis里面。
效果圖如下:
使用httpwebrequest發(fā)起請求
request.cs如下,主要就是兩個方法,一個方法是驗證代理ip是否有效,設(shè)置httpwebrequest的proxy屬性,請求百度,看到有些文章大多數(shù)會獲取響應(yīng)的內(nèi)容,如果內(nèi)容符合請求的網(wǎng)址則證明代理喲有效,實際上根據(jù)httpstatuscode 200就可以判斷是否驗證有效。
【注意】建的是控制臺程序,使用了異步,所以還是建.net core吧,c#語言的版本7.1。c#如何在控制臺程序中使用異步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
public class request { /// <summary> /// 驗證代理ip有效性 /// </summary> /// <param name="proxyip">代理ip</param> /// <param name="proxyport">代理ip 端口</param> /// <param name="timeout">詳情超時</param> /// <param name="url">請求的地址</param> /// <param name="success">成功的回調(diào)</param> /// <param name="fail">失敗的回調(diào)</param> /// <returns></returns> public static async system.threading.tasks.task getasync( string proxyip, int proxyport, int timeout, string url, action success, action< string > fail) { system.gc.collect(); httpwebrequest request = null ; httpwebresponse response = null ; try { request = (httpwebrequest)webrequest.create(url); //httpwebrequest request = httpwebrequest.createhttp(url); request.timeout =timeout; request.keepalive = false ; request.proxy = new webproxy(proxyip,proxyport); response = await request.getresponseasync() as httpwebresponse; if (response.statuscode == httpstatuscode.ok) { success(); } else { fail(response.statuscode+ ":" +response.statusdescription); } } catch (exception ex) { fail( "請求異常" +ex.message.tostring()); } finally { if (request != null ) { request.abort(); request = null ; } if (response != null ) { response.close(); } } } /// <summary> /// 發(fā)起http請求 /// </summary> /// <param name="url"></param> /// <param name="success">成功的回調(diào)</param> /// <param name="fail">失敗的回調(diào)</param> public static void get ( string url,action< string > success,action< string > fail) { streamreader reader = null ; stream stream = null ; webrequest request = null ; httpwebresponse response = null ; try { request = webrequest.create(url); request.timeout = 2000; response = (httpwebresponse)request.getresponse(); if (response.statuscode == httpstatuscode.ok) { stream = response.getresponsestream(); reader = new streamreader(stream); string result = reader.readtoend(); success(result); } else { fail(response.statuscode+ ":" +response.statusdescription); } } catch (exception ex) { fail(ex.tostring()); } finally { if (reader != null ) reader.close(); if (stream != null ) stream.close(); if (response!= null ) response.close(); if (request!= null ) request.abort(); } } } |
抓取免費(fèi)代理,并檢查是否有效
proxyiphelper.cs 中主要有四個方法,檢查ip是否可用checkproxyipasync、抓取xicidaili.com的代理getxicidailiproxy、抓取ip3366.net的代理getip3366proxy、抓取66ip.cn的代理getip3366proxy。如果想多抓取幾個網(wǎng)站可以多寫幾個。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
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">成功的回調(diào)</param> /// <param name="fail">失敗的回調(diào)</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網(wǎng)頁上去獲取代理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網(wǎng)頁上去獲取代理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會開啟新的線程,抓取三個網(wǎng)頁定義了三個timer對象。每一次抓取都會保存上一次抓取的集合,檢查前,會進(jìn)行對比,取出新的集合也就是沒有重復(fù)的那部分。有效性的ip比較低,這里沒有做統(tǒng)計,如果代碼再優(yōu)化一下,可以做一下統(tǒng)計,看看程序的主入口吧,最終的實現(xiàn)如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
|
class program { static bool timer_ip3366_iscompleted = true ; static bool timer_xicidaili_iscompleted = true ; static bool timer_66ip_iscompleted = true ; static timer timer_ip3366, timer_xicidaili, timer_66ip; private static list< string > lastlistip3366,lastlist66ip,lastlistxicidaili; //保存上一次抓取的代理,與下一次進(jìn)行對比,取新的集合進(jìn)行檢查篩選 static async task main( string [] args) { system.net.servicepointmanager.defaultconnectionlimit = 2000; console.writeline( "hellow proxyip" ); console.readline(); lastlist66ip = new list< string >(); lastlistip3366 = new list< string >(); lastlistxicidaili = new list< string >(); timer_ip3366 = new timer(async (state) => { await timerip3366async(); }, "processing timer_ip3366 event" , 0,1000*30); timer_xicidaili = new timer(async (state) => { await timerxicidailiasync(); }, "processing timer_xicidaili event" , 0, 1000 * 60); timer_66ip = new timer(async (state) => { await timer66ipasync(); }, "processing timer_66ip event" , 0, 1000*30); console.readline(); } private static async task timer66ipasync() { if (timer_66ip_iscompleted) { timer_66ip_iscompleted = false ; list< string > checklist = new list< string >(); var listproxyip = proxyiphelper.get66ipproxy(); if (listproxyip.count > 0) { console.foregroundcolor = consolecolor.darkcyan; console.writeline( "66ip.cn 抓取到" + listproxyip.count + "條記錄,正在對比........." ); listproxyip. foreach (f => { if (!lastlist66ip.contains(f)) { checklist.add(f); } }); lastlist66ip = listproxyip; if (checklist.count > 0) { console.foregroundcolor = consolecolor.darkcyan; console.writeline( "66ip.cn 需要檢查" + checklist.count + "條記錄,正在進(jìn)行檢測是否有效.........." ); for ( int i = 0; i < checklist.count; i++) { string ipaddress = checklist[i]; await proxyiphelper.checkproxyipasync(ipaddress, () => { bool insertsuccess = redishelper.insertset(ipaddress); console.foregroundcolor = consolecolor.white; console.writeline( "66ip.cn" ); if (insertsuccess) { console.writeline( "success" + ipaddress + "任務(wù)編號:" + i + "當(dāng)前任務(wù)線程:" + thread.currentthread.managedthreadid); } console.writeline( "重復(fù)插入" + ipaddress + "任務(wù)編號:" + i + "當(dāng)前任務(wù)線程:" + thread.currentthread.managedthreadid); }, (error) => { console.foregroundcolor = consolecolor.green; console.writeline( "66ip.cn" ); console.writeline( "error:" + ipaddress + error + "任務(wù)編號:" + i + "當(dāng)前任務(wù)線程:" + thread.currentthread.managedthreadid); }); } timer_66ip_iscompleted = true ; console.foregroundcolor = consolecolor.darkcyan; console.writeline( "66ip.cn" + checklist.count + "條記錄,已經(jīng)檢測完成,正在進(jìn)行下一次檢查" ); } else { timer_66ip_iscompleted = true ; console.foregroundcolor = consolecolor.darkcyan; console.writeline( "66ip.cn沒有需要檢查的代理ip" ); } } else { timer_66ip_iscompleted = true ; console.foregroundcolor = consolecolor.darkcyan; console.writeline( "66ip.cn沒有獲取到代理ip" ); } } } private static async task timerxicidailiasync() { if (timer_xicidaili_iscompleted) { //取出需要檢查的ip地址,第一次100條則checklist就是100條記錄, //第二次的100條中只有10是和上一次的不重復(fù),則第二次只需要檢查這10條記錄 timer_xicidaili_iscompleted = false ; list< string > checklist = new list< string >(); var listproxyip = proxyiphelper.getxicidailiproxy(1); if (listproxyip.count > 0) { console.writeline( "xicidaili.com 抓取到" + listproxyip.count + "條記錄,正在對比............" ); listproxyip. foreach (f => { if (!lastlistxicidaili.contains(f)) { checklist.add(f); } }); lastlistxicidaili = listproxyip; if (checklist.count > 0) { console.foregroundcolor = consolecolor.darkcyan; console.writeline( "xicidaili.com 需要檢查" + checklist.count + "條記錄,正在進(jìn)行檢測是否有效.........." ); for ( int i = 0; i < checklist.count; i++) { string ipaddress = checklist[i]; await proxyiphelper.checkproxyipasync(ipaddress, () => { bool insertsuccess = redishelper.insertset(ipaddress); console.foregroundcolor = consolecolor.white; console.writeline( "xicidaili.com" ); if (insertsuccess) { console.writeline( "success" + ipaddress + "任務(wù)編號:" + i + "當(dāng)前任務(wù)線程:" + thread.currentthread.managedthreadid); } else console.writeline( "重復(fù)插入" + ipaddress + "任務(wù)編號:" + i + "當(dāng)前任務(wù)線程:" + thread.currentthread.managedthreadid); }, (error) => { console.writeline( "xicidaili.com" ); console.foregroundcolor = consolecolor.red; console.writeline( "error:" + ipaddress + error + "任務(wù)編號:" + i + "當(dāng)前任務(wù)線程:" + thread.currentthread.managedthreadid); }); } timer_xicidaili_iscompleted = true ; console.foregroundcolor = consolecolor.darkcyan; console.writeline( "xicidaili.com" + checklist.count + "條記錄,已經(jīng)檢測完成,正在進(jìn)行下一次檢查" ); } else { timer_xicidaili_iscompleted = true ; console.foregroundcolor = consolecolor.darkcyan; console.writeline( "xicidaili.com沒有需要檢查的代理ip" ); } } else { timer_xicidaili_iscompleted = true ; console.foregroundcolor = consolecolor.darkcyan; console.writeline( "xicidaili.com沒有獲取到代理ip" ); } } } private static async task timerip3366async() { if (timer_ip3366_iscompleted) { timer_ip3366_iscompleted = false ; list< string > checklist = new list< string >(); var listproxyip = proxyiphelper.getip3366proxy(4); if (listproxyip.count > 0) { console.foregroundcolor = consolecolor.darkcyan; console.writeline( "ip3366.net 抓取到" + listproxyip.count + "條記錄,正在進(jìn)行檢測是否有效.........." ); listproxyip. foreach (f => { if (!lastlistip3366.contains(f)) { checklist.add(f); } }); lastlistip3366 = listproxyip; if (checklist.count != 0) { console.foregroundcolor = consolecolor.darkcyan; console.writeline( "ip3366.net 需要檢查" + checklist.count + "條記錄,正在進(jìn)行檢測是否有效.........." ); for ( int i = 0; i < checklist.count; i++) { string ipaddress = checklist[i]; await proxyiphelper.checkproxyipasync(ipaddress, () => { bool insertsuccess = redishelper.insertset(ipaddress); console.foregroundcolor = consolecolor.white; console.writeline( "ip3366.net" ); if (insertsuccess) { console.writeline( "success" + ipaddress + "任務(wù)編號:" + i + "當(dāng)前任務(wù)線程:" + thread.currentthread.managedthreadid); } else { console.foregroundcolor = consolecolor.red; console.writeline( "重復(fù)插入" + ipaddress + "任務(wù)編號:" + i + "當(dāng)前任務(wù)線程:" + thread.currentthread.managedthreadid); } }, (error) => { console.foregroundcolor = consolecolor.yellow; console.writeline( "ip3366.net" ); console.writeline( "error " + ipaddress + "任務(wù)編號:" + i + "當(dāng)前任務(wù)線程:" + thread.currentthread.managedthreadid); }); } timer_ip3366_iscompleted = true ; console.writeline( "ip3366.net" + checklist.count + "條記錄,已經(jīng)檢測完成,正在進(jìn)行下一次檢查" ); } else { timer_ip3366_iscompleted = true ; console.foregroundcolor = consolecolor.darkcyan; console.writeline( "ip3366.net沒有需要檢查的代理ip" ); } } else { timer_ip3366_iscompleted = true ; console.foregroundcolor = consolecolor.darkcyan; console.writeline( "ip3366.net沒有獲取到代理ip" ); } } } } |
redis第三庫使用的stackoverflow的 stackexchange.redis,代理ip不能重復(fù)儲存,所以采用的數(shù)據(jù)結(jié)構(gòu)是set。存的值非常簡單就一個ip加上port,也可以存入更多相關(guān)信息,感覺沒必要。即使有這些其他的信息,也很難發(fā)揮作用。redishelper.cs如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
public class redishelper { private static readonly object locker = new object (); private static connectionmultiplexer _redis; private const string connecttionstring = "127.0.0.1:6379,defaultdatabase=3" ; public const string redis_set_ket_success = "set_success_ip" ; private static connectionmultiplexer manager { get { if (_redis == null ) { lock (locker) { if (_redis != null ) return _redis; _redis = getmanager(); return _redis; } } return _redis; } } private static connectionmultiplexer getmanager( string connectionstring = null ) { if ( string .isnullorempty(connectionstring)) { connectionstring = connecttionstring; } return connectionmultiplexer.connect(connectionstring); } public static bool insertset( string value) { var db = manager.getdatabase(); return db.setadd(redis_set_ket_success,value); } } |
總結(jié)
明天補(bǔ)上刷服務(wù)器之家頁瀏覽量的文章吧,代碼還不夠好,ip的有效性還不高,對多線程的使用還不是很熟練
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對服務(wù)器之家的支持。
原文鏈接:https://www.cnblogs.com/zhangmumu/p/9269762.html