通过Python利用ADSL服务器和tinyproxy构建数据自己的动态代理IP池,用django+redis做web服务,提供IP接口

应公司业务需求需要在一些地方使用代理,要求连通率高,速度快,最主要的还要便宜,对比多家供应商后,最后还是决定自购拨号服务搭建代理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

猜你喜欢

转载自blog.csdn.net/MeteorCountry/article/details/82085238