有时候同一个IP去爬取同一网站上的内容,久了之后就会被该网站服务器屏蔽。解决方法就是更换IP。这个时候,在对方网站上,显示的不是我们真实地IP地址,而是代理服务器的IP地址。西刺代理http://www.xicidaili.com/nn/ 提供了很多可用的国内IP,云代理http://www.ip3366.net/提供了许多国外IP可以直接拿来使用。
但是这些代理有的短时间内可能就会失效,为了高效的使用代理,用Python实现一个爬虫,实时抓取代理,并将有效代理存入数据库或写入文件方便我们取用,并可以随时检测数据库和文件中的代理是否已经失效,将失效的代理删除。
- 下面是python代码实现:
# -*- coding=utf-8 -*-
__author__ = 'yansong'
import re
import requests
from lxml import etree
import urllib2, time, datetime
from lxml import etree
import sqlite3,time
import sys
import chardet
reload(sys)
sys.setdefaultencoding('utf-8') #设置系统默认编码为utf-8
class myProxy():
def __init__(self):
self.user_agent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"
self.header = {"User-Agent": self.user_agent} #请求头
self.dbname="myproxy.db" #数据库名
self.now = time.strftime("%Y-%m-%d")
#抓取国内高匿代理,并检查IP、PORT 是否可用,若可用则存入数据库,并写入文件,方便需要时取用
def getXicidaili(self, num):
nn_url = "http://www.xicidaili.com/nn/" + str(num)
#国内高匿
req = urllib2.Request(nn_url, headers=self.header)
resp = urllib2.urlopen(req, timeout=10)
content = resp.read()
et = etree.HTML(content) #将源码转换为能被XPath匹配的格式
#网页源码中class 分开了奇偶两个class,使用lxml最方便的方式就是分开获取。刚开始使用一个方式获取,出现很多IP和port抓不到的情况,分开获取可以抓到全部的ip 和port
result_even = et.xpath('//tr[@class=""]') #返回为一列表
result_odd = et.xpath('//tr[@class="odd"]')
for i in result_even:
t1 = i.xpath("./td/text()")[:6]
#print "IP:%s\tPort:%s\t Type:%s" % (t1[0], t1[1], t1[5])
if self.isAlive(t1[0], t1[1], t1[5]):
if t1[5] == 'HTTP': #区分代理IP类型,是 HTTP 还是HTTPS
proxies = {'http': 'http://' + t1[0] + ':' + t1[1]}
else:
proxies = {'https': 'https://' + t1[0] + ':' + t1[1]}
#检查IP是否可用,将可用的代理IP写入文件
self.check_Proxy_IP(proxies)
#pass
self.insert_db(self.now,t1[0],t1[1],t1[5])
for i in result_odd:
t2 = i.xpath("./td/text()")[:2]
#print "IP:%s\tPort:%s" % (t2[0], t2[1])
if self.isAlive(t2[0], t2[1]):
#pass
self.insert_db(self.now,t2[0],t2[1],t[5])
if t1[5] == 'HTTP':
proxies = {'http': 'http://' + t1[0] + ':' + t1[1]}
else:
proxies = {'https': 'https://' + t1[0] + ':' + t1[1]}
# 检查IP是否可用,将可用的代理IP写入文件
self.check_Proxy_IP(proxies)
#将可用的代理 IP、端口、类型 存进数据库,方便后续使用
def insert_db(self,date,ip,port,type):
dbname=self.dbname
try:
conn=sqlite3.connect(dbname)
except:
print "Error to open database%" %self.dbname
create_tb='''
CREATE TABLE IF NOT EXISTS MYPROXY
(DATE TEXT,
IP TEXT,
PORT TEXT,
TYPE TEXT
);
'''
conn.execute(create_tb)
insert_db_cmd='''
INSERT INTO MYPROXY (DATE,IP,PORT,TYPE) VALUES ('%s','%s','%s','%s');
''' %(date,ip,port,type)
conn.execute(insert_db_cmd)
conn.commit()
conn.close()
#查看爬到的代理IP是否还能用
def isAlive(self,ip,port,type):
if type == 'HTTP': #区分代理IP类型,是 HTTP 还是HTTPS
proxy = {'http':ip+':'+port}
else:
proxy = {'https': ip + ':' + port}
#print proxy
proxy_support=urllib2.ProxyHandler(proxy)
opener=urllib2.build_opener(proxy_support)
urllib2.install_opener(opener)
#使用代理访问腾讯官网,进行验证代理是否有效
#test_url="http://www.qq.com"
test_url = 'https://blog.csdn.net/qq_21933615/article/details/81089043'
req=urllib2.Request(test_url,headers=self.header)
try:
#timeout 代理延时设置为5,
resp=urllib2.urlopen(req,timeout=5)
print resp.read()
if resp.code == 200: #返回码200代表成功
print proxy
print "work"
return True
else:
print "not work"
return False
except :
#print "Not work"
return False
#检查数据库中的代理是否依然生效,将失效代理从数据库从删除
def check_db_pool(self):
conn=sqlite3.connect(self.dbname)
query_cmd='''
select IP,PORT,TYPE from MYPROXY;
'''
cursor=conn.execute(query_cmd)
for row in cursor:
print "ip:", row[0]
print "port :", row[1]
print "type :",row[2]
if not self.isAlive(row[0],row[1],row[2]):
#代理失效, 要从数据库从删除
delete_cmd='''
delete from PROXY where IP='%s'
''' %row[0]
print "delete IP %s in db" %row[0]
conn.execute(delete_cmd)
conn.commit()
conn.close()
#抓取国外代理
def getFrom_ip3366(self,num):
url='http://www.ip3366.net/?stype=1&page=' + str(num) #http://www.ip3366.net/
s=requests.get(url,headers=self.header)
print s.status_code
#print s.text # 注意s.text 与 s.content 区别
#typeEncode = sys.getfilesystemencoding() ##系统默认编码
#print typeEncode
infoencode = chardet.detect(s.content).get('encoding', 'utf-8') ##通过第3方模块来自动提取网页的编码
html2 = s.content.decode(infoencode, 'ignore').encode('utf-8') ##先转换成unicode编码,然后转换系统编码输出
#print html2
selector = etree.HTML(html2)
content = selector.xpath('//tbody/tr')
print content
for i in content:
print i
t1 = i.xpath("./td/text()")
print "IP:%s\tPort:%s\tType:%s" % (t1[0], t1[1],t1[3])
if self.isAlive(t1[0], t1[1], t1[3]):
if t1[5] == 'HTTP':
proxies = {'http': 'http://' + t1[0] + ':' + t1[1]}
else:
proxies = {'https': 'https://' + t1[0] + ':' + t1[1]}
print proxies
#检查proxy.cfg文件中的代理IP是否依然生效,将可用的代理IP保留下来,不能用的IP去掉
def validation(self):
fp = open('proxy.cfg', 'r')
lines=fp.readlines()
print lines
new_lines=[]
for i in lines:
x=eval(i.strip())
print x
s = requests.get(url='https://guyuan.anjuke.com/community/p1/', headers=self.header, proxies=x, timeout=10)
print s.status_code
if s.status_code == 200:
new_lines.append(i)
fp.close()
new_lines=list(set(new_lines))
with open('proxy.cfg','w') as fp:
for i in new_lines:
fp.write(i)
#检查代理IP是否可用,可用的话就写入文件proxy.cfg
def check_Proxy_IP(self, proxies):
fp=open('proxy.cfg','a')
#print proxies
try:
s=requests.get(url='http://www.qq.com',headers=self.header,proxies=proxies,timeout=10)
print s.status_code
if s.status_code==200:
#print str(proxies)
print proxies
print 'work'
fp.write(str(proxies))
fp.write('\n')
fp.close()
except Exception,e:
print e
if __name__ == "__main__":
obj=myProxy()
#obj.check_db_pool()
#obj.validation()
for i in range(50):
#obj.getFrom_ip3366(i)
obj.getXicidaili(i)