1. 程式人生 > >通過Python利用ADSL伺服器和tinyproxy構建資料自己的動態代理IP池,用django+redis做web服務,提供IP介面

通過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