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