应公司业务需求需要在一些地方使用代理,要求连通率高,速度快,最主要的还要便宜,对比多家供应商后,最后还是决定自购拨号服务搭建代理IP池。
需要配置:1.一台或多台adsl服务器(用以提供IP,可网上购买,通过ssh同域名连接)2.一台正常固定IP服务器拥来搭建IP代理池。(统一配置:python3.5以上环境)
具体配置:
1.在ADSL服务器上部署tinyporxy服务, 可以直接yum安装(yum -y install tinyproxy);
默认配置文件 /etc/tinyproxy/tinyproxy.conf,可执行文件 /usr/sbin/tinyproxy;
需要将配置文件中的第210行 Allow 127.0.0.1 注释掉,23行 Port 8888 改为自己喜欢的端口;
这样子代理服务器就配置好了,可以直接通过 tinyproxy 启动服务,killall tinyproxy 关闭服务;
ADSL服务器需要通过 pppoe 来进行拨号,需要有宽带账号,如果是直接购买的拨号服务器,一般都会配置好(我的就是);
拨号命令:pppoe-start (进行拨号),pppoe-stop(断开拨号连接),pppoe-status(查看当前拨号状态);
2. 用来做代理池的服务器需要安装 redis,python3安装django,redis包
(之所以使用django是因为之前的爬虫分发服务是基于Django实现的,所以现在只是在上面增加个代理池的);
代码逻辑:
之所以需要设置代理池,是因为我们所需要的代理IP就是拨号服务器不断变化的那个个IP,
代理池web服务最少应该提供三个接口:
1.在重新拨号(切换IP)前请求一次的接口 ,用来清除此台拨号服务器服务器上次提交的IP,
2.重新拨号成功后(成功切换IP)提交IP的接口,
3.用来提取IP的接口,
主要操作 redis 的 hash 类型(用来唯一标识一台拨号服务器和其提交的IP), 链表类型(用来做IP池)
为了应对本身业务需求和最大化利用代理IP增加:
4.提取到IP使用过后重新放入代理池的接口
根据测试得出单个代理对请求速度影响较小的最大并发数,链表内放入代理 IP*最大并发数 (我这里设置为22)
又另外增加redis 集合类型(防止提取到不可用IP)
代码部分:
代理池端部分代码,如需使用,可以直接引入到Django视图中,自己构建4个API接口,分别调用四个方法。
import redis
class ProxyPool:
def __init__(self):
self.pool = redis.ConnectionPool(host='127.0.0.1',port=6379, db=7)
#删除旧的IP
def update_del(self,only):
try:
conn = redis.Redis(connection_pool=self.pool)
#哈希表中取出本次提供IP服务器在上一次提供的IP
oldIP = conn.hget('Onlypool',only)
if oldIP:
#删除唯一标识
conn.hdel('Onlypool',only)
oldIP = str(oldIP,encoding='utf-8')
#在集合中删除上次的IP
conn.srem('IPset',oldIP)
conn.lrem('IPpool',oldIP,0)
return {'state':1,'message':'操作成功'}
else:
return {'state':2,'message':'未找到该机器'}
except Exception as e:
return {'state':0,'message':'操作失败--'+str(e)}
#增加新的IP
def update_put(self,ip,only):
try:
conn = redis.Redis(connection_pool=self.pool)
#判断此IP是否已经存在于池中
state = conn.sismember('IPset', ip)
if state:
return {'state': 0, 'message': '此IP以存在于池中'}
#以本次提供IP的唯一标识做键写入哈希表中
conn.hset('Onlypool',only,ip)
# 在集合中添加本次IP
conn.sadd('IPset', ip)
proxy = [ip] * 22
#放入IP池中
for i in proxy:
conn.rpush('IPpool',i)
return {'state':1,'message':'操作成功'}
except Exception as e:
return {'state':0,'message':'操作失败--'+ str(e)}
#获取指定数量的有效IP
def getIP(self,num=1):
try:
conn = redis.Redis(connection_pool=self.pool)
ipList = []
for i in range(num):
while True:
ip = conn.lpop('IPpool')
if ip:
state = conn.sismember('IPset', ip)
if state:
ip = str(ip,encoding='utf-8')
ipList.append(ip)
break
#数量不足时,放弃提取,并把已经提取的重新放回池中
else:
leninfor = len(ipList)
for ip in ipList:
conn.rpush('IPpool', ip)
return {'state': 0, 'message': 'IP不足,可用长度为:{0}'.format(leninfor)}
return {'state': 1, 'ipList':ipList,'message':'操作成功'}
except Exception as e:
return {'state': 0, 'message': '操作失败--' + str(e)}
#把使用过后的IP重新塞入池中
def putIP(self,ipList):
try:
conn = redis.Redis(connection_pool=self.pool)
for ip in ipList:
# 判断是否存在于集合中,存在即有效,不存在则放弃存入
state = conn.sismember('IPset', ip)
if state:
conn.rpush('IPpool',ip)
return {'state':1,'message':'操作成功'}
except Exception as e:
return {'state': 0, 'message': '操作失败--' + str(e)}
if __name__ == '__main__':
pass
拨号服务器端代码,这里设置没隔三分钟左右重新更换IP, 这里的机器唯一标识为图简单,自己设置
#!/usr/bin/python3
import re,time,os,requests,json,datetime,random
#唯一标识
only = 'only1'
#切换IP,重启代理服务
def changeIP():
os.system('pppoe-stop')
time.sleep(3)
os.popen('service tinyproxy stop')
os.popen('pppoe-start')
time.sleep(7)
os.popen('service tinyproxy start')
#取出当前IP
def extractIP():
infor = os.popen('pppoe-status').read()
try:
ip = re.search('(\d+\.\d+\.\d+\.\d+)',infor).group(1)
return ip
except:
return False
#删除旧的IP
def updateDel():
url = 'http://***.**.**.***:8000/proxy/updateDel?&only={0}'.format(only)
for i in range(3):
try:
res = requests.get(url,timeout=3)
infor = json.loads(res.text)
if infor['state']:
break
else:
continue
except Exception as e:
continue
#提交新的IP
def updatePut(ip):
ip = ip+':'+'8888'
url = 'http://***.**.**.***:8000/proxy/updatePut?ip={0}&only={1}'.format(ip,only)
for i in range(3):
try:
res = requests.get(url,timeout=3)
infor = json.loads(res.text)
if infor['state']:
break
else:
continue
except Exception as e:
continue
if __name__ == '__main__':
a = 1
while True:
if a==2:
time.sleep(random.randrange(170,230))
a=2
updateDel()
#删除IP后等待2秒在执行重新拨号,为防止池中取出不可用代理
time.sleep(2)
#print('已经删除旧IP')
while True:
changeIP()
ip = extractIP()
if ip:
break
updatePut(ip)
#print('已经更新IP')
注:本文原创,如需转载请注明来源,另希望可以指出不足,必将改正。
最新:https://blog.csdn.net/MeteorCountry/article/details/82729027