用php写过一个,不过由于 php 不支持多线程,抓取和验证速度都非常的慢
(尽管libcurl可以实现多线程抓取,但他也只限于抓取网页这个功能,抓回来的数据进行再处理很麻烦).
于是决定用python重新写,python支持多线程啊。
已经有一年多没有用过 python了,很多语法,语言特性都快忘记得差不多了。 经过三天业余时间的
摸索,今天我写的这个程序终于可以和大家交流了。
下面放出源代码: 希望有高手能帮我共同完善,
这个程序是我学python语言以来写的第二个程序,应该有很多写得不够简洁的地方,希望行家多多指点
程序现有功能:
1. 能自动从12个网站抓取代理列表,并保存到数据库里面
2. 自动验证每个代理是否可用,并保存验证时的响应时间做为判断代理速度的依据
3. 能分类输出代理信息,已验证的,未验证的,高度匿名代理,普通匿名代理,透明代理到不同文件
4支持的输出格式有 xml,htm,csv,txt,tab 每种文件都能自定义字段和格式
5. 扩展性比较强, 要添加一个新的抓取网站只需要改变一个全局变量,添加两个函数 (有详细接口说明)
6. 用 sqlite 做数据库,小巧,方便,简单,0配置,0安装,放在屁股口袋里就可以带走
7. 多线程抓取,多线程验证
我的运行环境:windows xp + python v2.4 ,其他版本未测试
程序下载: 点击这里(242kb)
代码的注释非常详细,python 初学者都可以看懂, 12个网站抓取分析的正则表达式都有详细注释
1 # -*- coding: gb2312 -*-
2 # vi:ts=4:et
3
4 """
5 目前程序能从下列网站抓取代理列表
6
7 /
8 http://www.pass-/
9 /
10 /
11 http://www.my-/
12 http://www.samair.ru/proxy/
13 /
14 http://proxylist.sakura.ne.jp/
15 /
16 /
17 /
18 /
19
20 问:怎样才能添加自己的新网站,并自动让程序去抓取?
21 答:
22
23 请注意源代码中以下函数的定义.从函数名的最后一个数字从1开始递增,目前已经到了13
24
25 def build_list_urls_1(page=5):
26 def parse_page_2(html=''):
27
28 def build_list_urls_2(page=5):
29 def parse_page_2(html=''):
30
31 .......
32
33 def build_list_urls_13(page=5):
34 def parse_page_13(html=''):
35
36
37 你要做的就是添加 build_list_urls_14 和 parse_page_14 这两个函数
38 比如你要从 抓取
39/somepath/showlist.asp?page=1
40... 到
41/somepath/showlist.asp?page=8 假设共8页
42
43 那么 build_list_urls_14 就应该这样定义
44 要定义这个page这个参数的默认值为你要抓取的页面数8,这样才能正确到抓到8个页面
45 def build_list_urls_14(page=8):
46.....
47return [ #返回的是一个一维数组,数组每个元素都是你要抓取的页面的绝对地址
48 '/somepath/showlist.asp?page=1',
49 '/somepath/showlist.asp?page=2',
50 '/somepath/showlist.asp?page=3',
51 ....
52 '/somepath/showlist.asp?page=8'
53]
54
55 接下来再写一个函数 parse_page_14(html='')用来分析上面那个函数返回的那些页面html的内容
56 并从html中提取代理地址
57 注意: 这个函数会循环处理 parse_page_14 中的所有页面,传入的html就是那些页面的html文本
58
59 ip: 必须为 xxx.xxx.xxx.xxx 数字ip格式,不能为 格式
60 port: 必须为 2-5位的数字
61 type: 必须为 数字 2,1,0,-1 中的其中一个。这些数字代表代理服务器的类型
62 2:高度匿名代理 1: 普通匿名代理 0:透明代理 -1: 无法确定的代理类型
63 #area: 代理所在国家或者地区, 必须转化为 utf8编码格式
64
65 def parse_page_14(html=''):
66....
67return [
68 [ip,port,type,area]
69 [ip,port,type,area]
70 .....
71 ....
72 [ip,port,type,area]
73]
74
75 最后,最重要的一点:修改全局变量 web_site_count的值,让他加递增1 web_site_count=14
76
77
78
79 问:我已经按照上面的说明成功的添加了一个自定义站点,我要再添加一个,怎么办?
80 答:既然已经知道怎么添加 build_list_urls_14 和 parse_page_14了
81
82 那么就按照同样的办法添加
83 def build_list_urls_15(page=5):
84 def parse_page_15(html=''):
85
86 这两个函数,并 更新全局变量 web_site_count=15
87
88 """
89
90
91 import urllib,time,random,re,threading,string
92
93 web_site_count=13 #要抓取的网站数目
94 day_keep=2#删除数据库中保存时间大于day_keep天的 无效代理
95 indebug=1
96
97 thread_num=100 # 开 thread_num 个线程检查代理
98 check_in_one_call=thread_num*25 # 本次程序运行时 最多检查的代理个数
99
100
101 skip_check_in_hour=1 # 在时间 skip_check_in_hour内,不对同一个代理地址再次验证
102 skip_get_in_hour=8# 每次采集新代理的最少时间间隔 (小时)
103
104 proxy_array=[]# 这个数组保存将要添加到数据库的代理列表
105 update_array=[] # 这个数组保存将要更新的代理的数据
106
107 db=None #数据库全局对象
108 conn=None
109 dbfile='proxier.db'#数据库文件名
110
111 target_url="/" # 验证代理的时候通过代理访问这个地址
112 target_string="030173"# 如果返回的html中包含这个字符串,
113 target_timeout=30# 并且响应时间小于 target_timeout 秒
114 #那么我们就认为这个代理是有效的
115
116
117
118 #到处代理数据的文件格式,如果不想导出数据,请让这个变量为空 output_type=''
119
120 output_type='xml' #以下格式可选, 默认xml
121 # xml
122 # htm
123 # tab 制表符分隔, 兼容 excel
124 # csv 逗号分隔, 兼容 excel
125 # txt xxx.xxx.xxx.xxx:xx 格式
126
127 # 输出文件名 请保证这个数组含有六个元素
128 output_filename=[
129 'uncheck', # 对于未检查的代理,保存到这个文件
130 'checkfail', # 已经检查,但是被标记为无效的代理,保存到这个文件
131 'ok_high_anon', # 高匿代理(且有效)的代理,按speed排序,最块的放前面
132 'ok_anonymous', # 普通匿名(且有效)的代理,按speed排序,最块的放前面
133 'ok_transparent',# 透明代理(且有效)的代理,按speed排序,最块的放前面
134 'ok_other' # 其他未知类型(且有效)的代理,按speed排序
135 ]
136
137
138 #输出数据的格式 支持的数据列有
139 # _ip_ , _port_ , _type_ , _status_ , _active_ ,
140 #_time_added_, _time_checked_ ,_time_used_ , _speed_, _area_
141
142 output_head_string='' # 输出文件的头部字符串
143 output_format='' # 文件数据的格式
144 output_foot_string='' # 输出文件的底部字符串
145
146
147
148ifoutput_type=='xml':
149output_head_string="<?xml version='1.0' encoding='gb2312'?><proxylist>/n"
150output_format="""<item>
151 <ip>_ip_</ip>
152 <port>_port_</port>
153 <speed>_speed_</speed>
154 <last_check>_time_checked_</last_check>
155 <area>_area_</area>
156 </item>
157 """
158output_foot_string="</proxylist>"
159elifoutput_type=='htm':
160output_head_string="""<table border=1 width='100%'>
161 <tr><td>代理</td><td>最后检查</td><td>速度</td><td>地区</td></tr>
162 """
163output_format="""<tr>
164<td>_ip_:_port_</td><td>_time_checked_</td><td>_speed_</td><td>_area_</td>
165</tr>
166"""
167output_foot_string="</table>"
168else:
169output_head_string=''
170output_foot_string=''
171
172ifoutput_type=="csv":
173output_format="_ip_, _port_, _type_, _speed_, _time_checked_, _area_/n"
174
175ifoutput_type=="tab":
176output_format="_ip_/t_port_/t_speed_/t_time_checked_/t_area_/n"
177
178ifoutput_type=="txt":
179output_format="_ip_:_port_/n"
180
181
182 # 输出文件的函数
183defoutput_file():
184globaloutput_filename,output_head_string,output_foot_string,output_type
185ifoutput_type=='':
186return
187fnum=len(output_filename)
188content=[]
189foriinrange(fnum):
190 content.append([output_head_string])
191
192conn.execute("select * from `proxier` order by `active`,`type`,`speed` asc")
193rs=conn.fetchall()
194
195foriteminrs:
196 type,active=item[2],item[4]
197ifactiveisNone:
198 content[0].append(formatline(item)) #未检查
199elifactive==0:
200 content[1].append(formatline(item)) #非法的代理
201elifactive==1andtype==2:
202 content[2].append(formatline(item)) #高匿
203elifactive==1andtype==1:
204 content[3].append(formatline(item)) #普通匿名
205elifactive==1andtype==0:
206 content[4].append(formatline(item)) #透明代理
207elifactive==1andtype==-1:
208 content[5].append(formatline(item)) #未知类型的代理
209else:
210pass
211
212foriinrange(fnum):
213 content[i].append(output_foot_string)
214 f=open(output_filename[i]+"."+output_type,'w')
215 f.write(string.join(content[i],''))
216 f.close()
217
218 #格式化输出每条记录
219defformatline(item):
220globaloutput_format
221arr=['_ip_','_port_','_type_','_status_','_active_',
222 '_time_added_','_time_checked_','_time_used_',
223 '_speed_','_area_']
224s=output_format
225foriinrange(len(arr)):
226 s=string.replace(s,arr[i],str(formatitem(item[i],i)))
227returns
228
229
230 #对于数据库中的每个不同字段,要处理一下,中文要编码,日期字段要转化
231defformatitem(value,colnum):
232globaloutput_type
233if(colnum==9):
234 value=value.encode('cp936')
235elifvalueisNone:
236 value=''
237
238ifcolnum==5orcolnum==6orcolnum==7:#time_xxxed
239 value=string.atof(value)
240ifvalue<1:
241 value=''
242else:
243 value=formattime(value)
244
245ifvalue==''andoutput_type=='htm':value=''
246returnvalue
247
248
249
250defcheck_one_proxy(ip,port):
251globalupdate_array
252globalcheck_in_one_call
253globaltarget_url,target_string,target_timeout
254
255url=target_url
256checkstr=target_string
257timeout=target_timeout
258ip=string.strip(ip)
259proxy=ip+':'+str(port)
260proxies = {'http': 'http://'+proxy+'/'}
261opener = urllib.FancyURLopener(proxies)
262opener.addheaders = [
263 ('User-agent','Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)')
264 ]
265t1=time.time()
266
267if(url.find("?")==-1):
268 url=url+'?rnd='+str(random.random())
269else:
270 url=url+'&rnd='+str(random.random())
271
272try:
273 f = opener.open(url)
274 s= f.read()
275 pos=s.find(checkstr)
276except:
277 pos=-1
278pass
279t2=time.time()
280timeused=t2-t1
281if(timeused<timeoutandpos>0):
282 active=1
283else:
284 active=0
285update_array.append([ip,port,active,timeused])
286printlen(update_array),' of ',check_in_one_call," ",ip,':',port,'--',int(timeused)
287
288
289defget_html(url=''):
290opener = urllib.FancyURLopener({})#不使用代理
291#www.my- 需要下面这个Cookie才能正常抓取
292opener.addheaders = [
293 ('User-agent','Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)'),
294 ('Cookie','permission=1')
295 ]
296t=time.time()
297if(url.find("?")==-1):
298 url=url+'?rnd='+str(random.random())
299else:
300 url=url+'&rnd='+str(random.random())
301try:
302 f = opener.open(url)
303returnf.read()
304except:
305return''
306
307
308
309
310 ################################################################################
#
## by Go_Rush(阿舜) from /
#
################################################################################
311
312defbuild_list_urls_1(page=5):
313page=page+1
314ret=[]
315foriinrange(1,page):
316 ret.append('/page%(num)01d.html'%{'num':i})
317returnret
318
319defparse_page_1(html=''):
320matches=re.findall(r'''
321 <td>([/d/.]+)<//td>[/s/n/r]* #ip
322 <td>([/d]+)<//td>[/s/n/r]*#port
323 <td>([^/<]*)<//td>[/s/n/r]* #type
324 <td>([^/<]*)<//td> #area
325 ''',html,re.VERBOSE)
326ret=[]
327formatchinmatches:
328 ip=match[0]
329 port=match[1]
330 type=match[2]
331 area=match[3]
332if(type=='anonymous'):
333 type=1
334elif(type=='high anonymity'):
335 type=2
336elif(type=='transparent'):
337 type=0
338else:
339 type=-1
340 ret.append([ip,port,type,area])
341ifindebug:print'1',ip,port,type,area
342returnret
343
344 ################################################################################
#
## by Go_Rush(阿舜) from /
#
################################################################################
345
346
347defbuild_list_urls_2(page=1):
348return['/ProxyList/fresh-proxy-list.shtml']
349
350defparse_page_2(html=''):
351matches=re.findall(r'''
352 ((?:[/d]{1,3}/.){3}[/d]{1,3})/:([/d]+)#ip:port
353 /s+(Anonymous|Elite Proxy)[+/s]+ #type
354 (.+)/r?/n#area
355 ''',html,re.VERBOSE)
356ret=[]
357formatchinmatches:
358 ip=match[0]
359 port=match[1]
360 type=match[2]
361 area=match[3]
362if(type=='Anonymous'):
363 type=1
364else:
365 type=2
366 ret.append([ip,port,type,area])
367ifindebug:print'2',ip,port,type,area
368returnret
369
370
371 ################################################################################
#
## by Go_Rush(阿舜) from /
#
################################################################################
372
373
374defbuild_list_urls_3(page=15):
375page=page+1
376ret=[]
377foriinrange(1,page):
378 ret.append('http://www.samair.ru/proxy/proxy-%(num)02d.htm'%{'num':i})
379returnret
380
381defparse_page_3(html=''):
382matches=re.findall(r'''
383 <tr><td><span/sclass/="/w+">(/d{1,3})<//span>/. #ip(part1)
384 <span/sclass/="/w+">
385 (/d{1,3})<//span> #ip(part2)
386 (/./d{1,3}/./d{1,3}) #ip(part3,part4)
387
388 /:/r?/n(/d{2,5})<//td>#port
389 <td>([^<]+)</td> #type
390 <td>[^<]+<//td>
391 <td>([^<]+)<//td> #area
392 <//tr>''',html,re.VERBOSE)
393ret=[]
394formatchinmatches:
395 ip=match[0]+"."+match[1]+match[2]
396 port=match[3]
397 type=match[4]
398 area=match[5]
399if(type=='anonymous proxy server'):
400 type=1
401elif(type=='high-anonymous proxy server'):
402 type=2
403elif(type=='transparent proxy'):
404 type=0
405else:
406 type=-1
407 ret.append([ip,port,type,area])
408ifindebug:print'3',ip,port,type,area
409returnret
410
411
412
413 ################################################################################
#
## by Go_Rush(阿舜) from /
#
################################################################################
414
415defbuild_list_urls_4(page=3):
416page=page+1
417ret=[]
418foriinrange(1,page):
419 ret.append('http://www.pass-/proxy/index.php?page=%(n)01d'%{'n':i})
420returnret
421
422defparse_page_4(html=''):
423matches=re.findall(r"""
424 list
425 /('(/d{1,3}/./d{1,3}/./d{1,3}/./d{1,3})' #ip
426 /,'(/d{2,5})'#port
427 /,'(/d)'#type
428 /,'([^']+)'/)#area
429 /;/r?/n""",html,re.VERBOSE)
430ret=[]
431formatchinmatches:
432 ip=match[0]
433 port=match[1]
434 type=match[2]
435 area=match[3]
436 area=unicode(area, 'cp936')
437 area=area.encode('utf8')
438if(type=='1'):#type的判断可以查看抓回来的网页的javascript部分
439 type=1
440elif(type=='3'):
441 type=2
442elif(type=='2'):
443 type=0
444else:
445 type=-1
446 ret.append([ip,port,type,area])
447ifindebug:print'4',ip,port,type,area
448returnret
449
450
451 ################################################################################
#
## by Go_Rush(阿舜) from /
#
################################################################################
452
453
454defbuild_list_urls_5(page=12):
455page=page+1
456ret=[]
457for i in range(1,page):
458 ret.append('/index2.asp?page=%(num)01d'%{'num':i})
459return ret
460
461 def parse_page_5(html=''):
462matches=re.findall(r"<font color=black>([^<]*)</font>",html)
463ret=[]
464for index, match in enumerate(matches):
465 if (index%3==0):
466 ip=matches[index+1]
467 port=matches[index+2]
468 type=-1#该网站未提供代理服务器类型
469 area=unicode(match, 'cp936')
470 area=area.encode('utf8')
471 if indebug:print '5',ip,port,type,area
472 ret.append([ip,port,type,area])
473 else:
474 continue
475return ret
476
477 ################################################################################
#
## by Go_Rush(阿舜) from /
#
################################################################################
478
479
480 def build_list_urls_6(page=3):
481page=page+1
482ret=[]
483for i in range(1,page):
484 ret.append('/proxy%(num)01d.html'%{'num':i})
485return ret
486
487 def parse_page_6(html=''):
488matches=re.findall(r'''<tr>
489 <td>([^&]+) #ip
490 ‌
491 /:([^<]+) #port
492 </td>
493 <td>HTTP</td>
494 <td>[^<]+</td>
495 <td>([^<]+)</td>#area
496 </tr>''',html,re.VERBOSE)
497ret=[]
498for match in matches:
499 ip=match[0]
500 port=match[1]
501 type=-1#该网站未提供代理服务器类型
502 area=match[2]
503 area=unicode(area, 'cp936')
504 area=area.encode('utf8')
505 ret.append([ip,port,type,area])
506 if indebug:print '6',ip,port,type,area
507return ret
508
509
510
511 ################################################################################
#
## by Go_Rush(阿舜) from /
#
################################################################################
512
513
514
515 def build_list_urls_7(page=1):
516return ['/http_highanon.txt']
517
518 def parse_page_7(html=''):
519matches=re.findall(r'(/d{1,3}/./d{1,3}/./d{1,3}/./d{1,3})/:(/d{2,5})',html)
520ret=[]
521for match in matches:
522 ip=match[0]
523 port=match[1]
524 type=2
525 area='--'
526 ret.append([ip,port,type,area])
527 if indebug:print '7',ip,port,type,area
528return ret
529
530
531
532 ################################################################################
#
## by Go_Rush(阿舜) from /
#
################################################################################
533
534
535
536
537defbuild_list_urls_8(page=1):
538return['/http.txt']
539
540defparse_page_8(html=''):
541matches=re.findall(r'(/d{1,3}/./d{1,3}/./d{1,3}/./d{1,3})/:(/d{2,5})',html)
542ret=[]
543formatchinmatches:
544 ip=match[0]
545 port=match[1]
546 type=-1
547 area='--'
548 ret.append([ip,port,type,area])
549ifindebug:print'8',ip,port,type,area
550returnret
551
552
553
554 ################################################################################
#
## by Go_Rush(阿舜) from /
#
################################################################################
555
556
557defbuild_list_urls_9(page=6):
558page=page+1
559ret=[]
560foriinrange(0,page):
561 ret.append('http://proxylist.sakura.ne.jp/index.htm?pages=%(n)01d'%{'n':i})
562returnret
563
564defparse_page_9(html=''):
565matches=re.findall(r'''
566 (/d{1,3}/./d{1,3}/./d{1,3}/./d{1,3}) #ip
567 /:(/d{2,5}) #port
568 <//TD>[/s/r/n]*
569 <TD>([^<]+)</TD> #area
570 [/s/r/n]*
571 <TD>([^<]+)</TD> #type
572''',html,re.VERBOSE)
573ret=[]
574formatchinmatches:
575 ip=match[0]
576 port=match[1]
577 type=match[3]
578 area=match[2]
579if(type=='Anonymous'):
580 type=1
581else:
582 type=-1
583 ret.append([ip,port,type,area])
584ifindebug:print'9',ip,port,type,area
585returnret
586
587 ################################################################################
#
## by Go_Rush(阿舜) from /
#
################################################################################
588
589defbuild_list_urls_10(page=5):
590page=page+1
591ret=[]
592foriinrange(1,page):
593 ret.append('/page%(n)01d.html'%{'n':i})
594returnret
595
596defparse_page_10(html=''):
597matches=re.findall(r'''
598 (/d{1,3}/./d{1,3}/./d{1,3}/./d{1,3}) #ip
599 <//td>[/s/r/n]*
600 <td[^>]+>(/d{2,5})<//td>#port
601 [/s/r/n]*
602 <td>([^<]+)<//td> #type
603 [/s/r/n]*
604 <td>([^<]+)<//td> #area
605 ''',html,re.VERBOSE)
606ret=[]
607formatchinmatches:
608 ip=match[0]
609 port=match[1]
610 type=match[2]
611 area=match[3]
612if(type=='high anonymity'):
613 type=2
614elif(type=='anonymous'):
615 type=1
616elif(type=='transparent'):
617 type=0
618else:
619 type=-1
620 ret.append([ip,port,type,area])
621ifindebug:print'10',ip,port,type,area
622returnret
623
624 ################################################################################
#
## by Go_Rush(阿舜) from /
#
################################################################################
625
626
627
628defbuild_list_urls_11(page=10):
629page=page+1
630ret=[]
631foriinrange(1,page):
632 ret.append('http://www.my-/list/proxy.php?list=%(n)01d'%{'n':i})
633
634ret.append('http://www.my-/list/proxy.php?list=s1')
635ret.append('http://www.my-/list/proxy.php?list=s2')
636ret.append('http://www.my-/list/proxy.php?list=s3')
637returnret
638
639defparse_page_11(html=''):
640matches=re.findall(r'(/d{1,3}/./d{1,3}/./d{1,3}/./d{1,3})/:(/d{2,5})',html)
641ret=[]
642
643if(html.find('(Level 1)')>0):
644 type=2
645elif(html.find('(Level 2)')>0):
646 type=1
647elif(html.find('(Level 3)')>0):
648 type=0
649else:
650 type=-1
651
652formatchinmatches:
653 ip=match[0]
654 port=match[1]
655 area='--'
656 ret.append([ip,port,type,area])
657ifindebug:print'11',ip,port,type,area
658returnret
659
660 ################################################################################
#
## by Go_Rush(阿舜) from /
#
################################################################################
661
662
663
664defbuild_list_urls_12(page=4):
665ret=[]
666ret.append('/plr4.html')
667ret.append('/pla4.html')
668ret.append('/pld4.html')
669ret.append('/pls4.html')
670returnret
671
672defparse_page_12(html=''):
673matches=re.findall(r'''
674 onMouseOver/=
675 "s/(/'(/w/w)/'/)" #area
676 /sonMouseOut/="d/(/)"/s?c?l?a?s?s?/=?"?
677 (/w?) #type
678 "?>
679 (/d{1,3}/./d{1,3}/./d{1,3}/./d{1,3}) #ip
680 /:(/d{2,5}) #port
681 ''',html,re.VERBOSE)
682ret=[]
683formatchinmatches:
684 ip=match[2]
685 port=match[3]
686 area=match[0]
687 type=match[1]
688if(type=='A'):
689 type=2
690elif(type=='B'):
691 type=1
692else:
693 type=0
694 ret.append([ip,port,type,area])
695ifindebug:print'12',ip,port,type,area
696returnret
697
698 ################################################################################
#
## by Go_Rush(阿舜) from /
#
################################################################################
699
700
701defbuild_list_urls_13(page=3):
702url='/'
703html=get_html(url)
704matchs=re.findall(r"""
705 href/='([^']+)'>(?:high_anonymous|anonymous|transparent)
706 /sproxy/slist<//a>""",html,re.VERBOSE)
707returnmap(lambdax: url+x, matchs)
708
709defparse_page_13(html=''):
710html_matches=re.findall(r"eval/(unescape/('([^']+)'/)",html)
711if(len(html_matches)>0):
712 conent=urllib.unquote(html_matches[0])
713matches=re.findall(r"""<td>(/d{1,3}/./d{1,3}/./d{1,3}/./d{1,3})<//td>
714 <td>(/d{2,5})<//td><//tr>""",conent,re.VERBOSE)
715ret=[]
716if(html.find('<title>Checked Proxy Lists - proxylist_high_anonymous_')>0):
717 type=2
718elif(html.find('<title>Checked Proxy Lists - proxylist_anonymous_')>0):
719 type=1
720elif(html.find('<title>Checked Proxy Lists - proxylist_transparent_')>0):
721 type=0
722else:
723 type=-1
724
725formatchinmatches:
726 ip=match[0]
727 port=match[1]
728 area='--'
729 ret.append([ip,port,type,area])
730ifindebug:print'13',ip,port,type,area
731returnret
732
733 ################################################################################
#
## by Go_Rush(阿舜) from /
#
################################################################################
734
735
736
737 #线程类
738
739classTEST(threading.Thread):
740def__init__(self,action,index=None,checklist=None):
741 threading.Thread.__init__(self)
742 self.index =index
743 self.action=action
744 self.checklist=checklist
745
746defrun(self):
747if(self.action=='getproxy'):
748 get_proxy_one_website(self.index)
749else:
750 check_proxy(self.index,self.checklist)
751
752
753defcheck_proxy(index,checklist=[]):
754foriteminchecklist:
755 check_one_proxy(item[0],item[1])
756
757
758defpatch_check_proxy(threadCount,action=''):
759globalcheck_in_one_call,skip_check_in_hour,conn
760threads=[]
761if(action=='checknew'): #检查所有新加入,并且从未被检查过的
762 orderby=' `time_added` desc '
763 strwhere=' `active` is null '
764elif(action=='checkok'): #再次检查 以前已经验证成功的 代理
765 orderby=' `time_checked` asc '
766 strwhere=' `active`=1 '
767elif(action=='checkfail'): #再次检查以前验证失败的代理
768 orderby=' `time_checked` asc '
769 strwhere=' `active`=0 '
770else: #检查所有的
771 orderby=' `time_checked` asc '
772 strwhere=' 1=1 '
773sql="""
774 select `ip`,`port` FROM `proxier` where
775 `time_checked` < (unix_timestamp()-%(skip_time)01s)
776 and %(strwhere)01s
777 order by %(order)01s
778 limit %(num)01d
779 """%{'num':check_in_one_call,
780 'strwhere':strwhere,
781 'order':orderby,
782 'skip_time':skip_check_in_hour*3600}
783conn.execute(sql)
784rows = conn.fetchall()
785
786check_in_one_call=len(rows)
787
788#计算每个线程将要检查的代理个数
789iflen(rows)>=threadCount:
790 num_in_one_thread=len(rows)/threadCount
791else:
792 num_in_one_thread=1
793
794threadCount=threadCount+1
795print"现在开始验证以下代理服务器....."
796forindexinrange(1,threadCount):
797#分配每个线程要检查的checklist,并把那些剩余任务留给最后一个线程
798 checklist=rows[(index-1)*num_in_one_thread:index*num_in_one_thread]
799if(index+1==threadCount):
800 checklist=rows[(index-1)*num_in_one_thread:]
801
802 t=TEST(action,index,checklist)
803 t.setDaemon(True)
804 t.start()
805 threads.append((t))
806forthreadinthreads:
807 thread.join(60)
808update_proxies() #把所有的检查结果更新到数据库
809
810
811defget_proxy_one_website(index):
812globalproxy_array
813func='build_list_urls_'+str(index)
814parse_func=eval('parse_page_'+str(index))
815urls=eval(func+'()')
816forurlinurls:
817 html=get_html(url)
818printurl
819 proxylist=parse_func(html)
820forproxyinproxylist:
821 ip=string.strip(proxy[0])
822 port=string.strip(proxy[1])
823if(pile("^/d{1,3}/./d{1,3}/./d{1,3}/./d{1,3}$").search(ip)):
824 type=str(proxy[2])
825 area=string.strip(proxy[3])
826 proxy_array.append([ip,port,type,area])
827
828
829defget_all_proxies():
830globalweb_site_count,conn,skip_get_in_hour
831
832#检查最近添加代理是什么时候,避免短时间内多次抓取
833rs=conn.execute("select max(`time_added`) from `proxier` limit 1")
834last_add=rs.fetchone()[0]
835if(last_addandmy_unix_timestamp()-last_add<skip_get_in_hour*3600):
836print"""
837 放弃抓取代理列表!
838 因为最近一次抓取代理的时间是: %(t)1s
839 这个时间距离现在的时间小于抓取代理的最小时间间隔: %(n)1d 小时
840 如果一定要现在抓取代理,请修改全局变量: skip_get_in_hour 的值
841 """%{'t':formattime(last_add),'n':skip_get_in_hour}
842return
843
844print"现在开始从以下"+str(web_site_count)+"个网站抓取代理列表...."
845threads=[]
846count=web_site_count+1
847forindexinrange(1,count):
848 t=TEST('getproxy',index)
849 t.setDaemon(True)
850 t.start()
851 threads.append((t))
852forthreadinthreads:
853 thread.join(60)
854add_proxies_to_db()
855
856defadd_proxies_to_db():
857globalproxy_array
858count=len(proxy_array)
859foriinrange(count):
860 item=proxy_array[i]
861 sql="""insert into `proxier` (`ip`,`port`,`type`,`time_added`,`area`) values('
862"""+item[0]+"',"+item[1]+","+item[2]+",unix_timestamp(),'"+clean_string(item[3])+"')"
863try:
864 conn.execute(sql)
865print"%(num)2.1f/%/t"%{'num':100*(i+1)/count},item[0],":",item[1]
866except:
867pass
868
869
870defupdate_proxies():
871globalupdate_array
872foriteminupdate_array:
873 sql='''
874 update `proxier` set `time_checked`=unix_timestamp(),
875 `active`=%(active)01d,
876 `speed`=%(speed)02.3f
877 where `ip`='%(ip)01s' and `port`=%(port)01d
878 '''%{'active':item[2],'speed':item[3],'ip':item[0],'port':item[1]}
879try:
880 conn.execute(sql)
881except:
882pass
883
884 #sqlite 不支持 unix_timestamp这个函数,所以我们要自己实现
885defmy_unix_timestamp():
886returnint(time.time())
887
888defclean_string(s):
889tmp=re.sub(r"['/,/s]", ' ', s)
890returnre.sub(r"/s+", ' ', tmp)
891
892defformattime(t):
893returntime.strftime('%c',time.gmtime(t+8*3600))
894
895
896defopen_database():
897globaldb,conn,day_keep,dbfile
898
899try:
900 from pysqlite2 import dbapi2 as sqlite
901except:
902print"""
903 本程序使用 sqlite 做数据库来保存数据,运行本程序需要 pysqlite的支持
904 python 访问 sqlite 需要到下面地址下载这个模块 pysqlite, 272kb
905 /tracker/pysqlite/wiki/pysqlite#Downloads
906 下载(Windows binaries for Python 2.x)
907 """
908raiseSystemExit
909
910try:
911 db = sqlite.connect(dbfile,isolation_level=None)
912 db.create_function("unix_timestamp", 0, my_unix_timestamp)
913 conn = db.cursor()
914except:
915print"操作sqlite数据库失败,请确保脚本所在目录具有写权限"
916raiseSystemExit
917
918sql="""
919 /* ip:只要纯ip地址(xxx.xxx.xxx.xxx)的代理 */
920 /* type: 代理类型 2:高匿 1:普匿 0:透明 -1: 未知 */
921 /* status: 这个字段本程序还没有用到,留在这里作以后扩展*/
922 /* active: 代理是否可用 1:可用 0:不可用 */
923 /* speed: 请求相应时间,speed越小说明速度越快 */
924
925 CREATE TABLE IF NOT EXISTS `proxier` (
926 `ip` varchar(15) NOT NULL default '',
927 `port` int(6) NOT NULL default '0',
928 `type` int(11) NOT NULL default '-1',
929 `status` int(11) default '0',
930 `active` int(11) default NULL,
931 `time_added` int(11) NOT NULL default '0',
932 `time_checked` int(11) default '0',
933 `time_used` int(11) default '0',
934 `speed` float default NULL,
935 `area` varchar(120) default '--',/* 代理服务器所在位置 */
936 PRIMARY KEY (`ip`)
937 );
938 /*
939 CREATE INDEX IF NOT EXISTS `type` ON proxier(`type`);
940 CREATE INDEX IF NOT EXISTS `time_used` ON proxier(`time_used`);
941 CREATE INDEX IF NOT EXISTS `speed` ON proxier(`speed`);
942 CREATE INDEX IF NOT EXISTS `active`ON proxier(`active`);
943 */
944 PRAGMA encoding = "utf-8";/* 数据库用 utf-8编码保存 */
945"""
946conn.executescript(sql)
947conn.execute("""DELETE FROM `proxier`
948where `time_added`< (unix_timestamp()-?)
949and `active`=0""",(day_keep*86400,))
950
951conn.execute("select count(`ip`) from `proxier`")
952m1=conn.fetchone()[0]
953ifm1isNone:return
954
955conn.execute("""select count(`time_checked`)
956from `proxier` where `time_checked`>0""")
957m2=conn.fetchone()[0]
958
959ifm2==0:
960 m3,m4,m5=0,"尚未检查","尚未检查"
961else:
962 conn.execute("select count(`active`) from `proxier` where `active`=1")
963 m3=conn.fetchone()[0]
964 conn.execute("""select max(`time_checked`), min(`time_checked`)
965from `proxier` where `time_checked`>0 limit 1""")
966 rs=conn.fetchone()
967 m4,m5=rs[0],rs[1]
968 m4=formattime(m4)
969 m5=formattime(m5)
970print"""
971共%(m1)1d条代理,其中%(m2)1d个代理被验证过,%(m3)1d个代理验证有效。
972 最近一次检查时间是:%(m4)1s
973 最远一次检查时间是: %(m5)1s
974提示:对于检查时间超过24小时的代理,应该重新检查其有效性
975"""%{'m1':m1,'m2':m2,'m3':m3,'m4':m4,'m5':m5}
976
977
978
979defclose_database():
980globaldb,conn
981conn.close()
982db.close()
983conn=None
984db=None
985
986if__name__ == '__main__':
987open_database()
988get_all_proxies()
989patch_check_proxy(thread_num)
990output_file()
991close_database()
992print"所有工作已经完成"
转自 /ashun/archive//06/01/python_proxy_checker.html