如何构建一个自己的代理ip池
一、默認(rèn)自動(dòng)切換IP
登錄線程IP池客戶端時(shí),默認(rèn)情況下會(huì)自動(dòng)切換IP。 如果不想自動(dòng)切換IP,或者還沒有準(zhǔn)備開始使用,請?jiān)诳蛻舳擞覀?cè)將“在IP過期前幾秒自動(dòng)申請切換”設(shè)置為“0”。 0無效。
二.默認(rèn)情況下不需要授權(quán)
默認(rèn)情況下,線程IP池可以使用代理IP,而無需驗(yàn)證。 如果需要在特定情況下允許使用,請選中“訪問代理需要驗(yàn)證”。 用戶名和密碼是用于登錄線程IP池客戶端的“TID”和“密碼”,帳戶驗(yàn)證允許使用。
三.退出軟件前取消代理
許多用戶退出線程IP池客戶端后,發(fā)現(xiàn)瀏覽器無法訪問網(wǎng)站。 這是因?yàn)?#xff0c;以前在軟件中一鍵設(shè)置瀏覽器代理IP,軟件結(jié)束后,無法繼續(xù)傳輸,因此無法訪問網(wǎng)站。
在退出線程IP池軟件之前,最好取消代理。 如果忘記,請選擇瀏覽器右上角的“工具”——“互聯(lián)網(wǎng)選項(xiàng)”——“連接”——“局域網(wǎng)設(shè)置”(雖然因?yàn)g覽器而異,但方法大致相同)、“為局域網(wǎng)使用代理服務(wù)器”
1、爬取免費(fèi)代理IP,搭建動(dòng)態(tài)IP池
市面上有不少免費(fèi)的代理IP服務(wù),使用免費(fèi)代理搭建動(dòng)態(tài)IP池的方法非常常見,也是比較多人使用的一種。因?yàn)樗敲赓M(fèi)的,也就意味著無需成本,所以大多數(shù)人都因?yàn)椤懊赓M(fèi)”二字而趨之若鶩。但是這種方法操作相對復(fù)雜一些,網(wǎng)上也有不少的教程分享,如果您感興趣的話可以上網(wǎng)查找相關(guān)的項(xiàng)目,這邊就不詳細(xì)敘述如何爬取搭建了。
2、購買撥號(hào)服務(wù)器,搭建動(dòng)態(tài)IP池
購買撥號(hào)服務(wù)器來搭建動(dòng)態(tài)IP池也是很多朋友的選擇。購買一定數(shù)量的服務(wù)器,然后花費(fèi)一些時(shí)間來編寫代碼,或者結(jié)合網(wǎng)上的現(xiàn)成的軟件,就可以將代理動(dòng)態(tài)IP的池搭建起來了。這種方法搭建出來的IP池資源都是一個(gè)人獨(dú)享的,工作起來效果還是不錯(cuò)的。只不過長期下來的話,服務(wù)器的維護(hù)成本較高,并且需要定時(shí)的維護(hù),消耗大量的時(shí)間,如果是個(gè)人的話,搭建起來后期維護(hù)的成本太高了,如果您不是高端玩家的話,不建議使用這種方式搭建IP池。
3、購買代理IP,搭建動(dòng)態(tài)IP池
選擇購買代理IP服務(wù)的朋友很多,因?yàn)榇鞩P服務(wù)省時(shí)省力并且效果比較好。相對于免費(fèi)的代理IP來說,收費(fèi)代理IP雖然需要付出一定的成本,但是IP資源都是真實(shí)IP,并且高匿性,穩(wěn)定性也好。
相比前兩種搭建IP池的方法來說,付費(fèi)代理IP更能滿足用戶的需求,但對于有些特殊要求的朋友來說,他們想一次提取很多個(gè)或者多次提取很多個(gè),存放在本地建立的IP池里,這種方法在一定的程度上優(yōu)化了方案。其中,像Lum Proxy這樣的動(dòng)態(tài)IP代理服務(wù)就深受大家的歡迎。Lum Proxy是國內(nèi)領(lǐng)先的動(dòng)態(tài)IP服務(wù)商,有著全球每一個(gè)國家和城市的9000多萬IP。在每個(gè)國家每個(gè)大城市基本都涵蓋,而且會(huì)不定時(shí)更換一些IP段。100%匿名高速代理,能夠有效地幫助大家更好地工作。
代理IP池管理代理的集合及其相關(guān)IP地址。IP池中的代理類型決定了代理IP池的類型。一般情況下,根據(jù)代理ip池包含的代理類型,代理ip池有5種類型。 1、免費(fèi)代理IP池。該免費(fèi)代理IP池也稱為公共IP池,它是管理免費(fèi)代理的列表。IP一般是數(shù)據(jù)中心IP,但是在一些公共IP池中,你可以使用混合的住宅IP??煽慷?#xff0c;公共IP是最不可靠的代理池。 由于很容易檢測到公共代理服務(wù)器(也稱為免費(fèi)代理服務(wù)器),一些網(wǎng)站會(huì)默認(rèn)阻止它,并在你不知情的情況下泄露你的真實(shí)IP地址。它們通常不需要任何形式的認(rèn)證,直到被使用。 在Internet上,如果使用免費(fèi)代理列表站點(diǎn),則很容易建立公共IP池。為了創(chuàng)建一個(gè)公共IP池,您需要在Internet上使用一個(gè)刮板為您獲取免費(fèi)代理,一個(gè)代理檢查器來確保該池只包含有效的代理,以及IP循環(huán)系統(tǒng)等等。然而,你需要知道,使用公共IP池來進(jìn)行任何合理的在線活動(dòng)都不是個(gè)好主意——你會(huì)受到壞鄰居、垃圾郵件和它們不可靠的特性的影響。2、數(shù)據(jù)中心代理IP池。數(shù)據(jù)中心代理IP池是只包含數(shù)據(jù)中心IP的代理池。DataCenterIP是數(shù)據(jù)中心擁有和管理的IP。DataCenterBroker池具有與數(shù)據(jù)中心代理相關(guān)的所有優(yōu)點(diǎn),但也具有與數(shù)據(jù)中心代理相關(guān)的所有缺點(diǎn)。在數(shù)據(jù)中心代理池中的代理存在許多障礙,因?yàn)樗鼈円子跈z測。
對于爬蟲來說,當(dāng)你的訪問頻率達(dá)到了目標(biāo)網(wǎng)站的預(yù)警值時(shí),就可能觸發(fā)目標(biāo)網(wǎng)站的反爬機(jī)制。而封禁訪問者ip就是很常見的一個(gè)反爬機(jī)制。
當(dāng)ip被封禁后,從此ip發(fā)出的請求將不能得到正確的響應(yīng)。這種時(shí)候,我們就需要一個(gè)代理ip池。
什么是代理ip池?
通俗地比喻一下,它就是一個(gè)池子,里面裝了很多代理ip。它有如下的行為特征:
1.池子里的ip是有生命周期的,它們將被定期驗(yàn)證,其中失效的將被從池子里面剔除。
2.池子里的ip是有補(bǔ)充渠道的,會(huì)有新的代理ip不斷被加入池子中。
3.池子中的代理ip是可以被隨機(jī)取出的。
這樣,代理池中始終有多個(gè)不斷更換的、有效的代理ip,且我們可以隨機(jī)從池子中取出代理ip,然后讓爬蟲程序使用代理ip訪問目標(biāo)網(wǎng)站,就可以避免爬蟲被ban的情況。
今天,我們就來說一下如何構(gòu)建自己的代理ip池。而且,我們要做一個(gè)比較靈活的代理池,它提供兩種代理方式:
1.每次都通過http接口提取一個(gè)隨機(jī)代理ip,然后在爬蟲中使用此代理ip(大部分代理ip池服務(wù)都是這種形式)
2.使用squid3代理做請求轉(zhuǎn)發(fā),爬蟲設(shè)置好squid3代理的地址,每次請求將由squid3自動(dòng)轉(zhuǎn)發(fā)給代理池中的代理
項(xiàng)目已經(jīng)放到了github上,不想看原理、只想應(yīng)用的可以直接移步github:open_proxy_pool
地址:https://github.com/AaronJny/open_proxy_pool
原理請往下看。
轉(zhuǎn)載請注明出處:https://blog.csdn.net/aaronjny/article/details/87865942
代理池結(jié)構(gòu)
代理池的組件可以大致描述如下:
1.代理IP的獲取/補(bǔ)充渠道,定期把獲取到的代理ip加入到代理池中
2.代理ip的驗(yàn)證機(jī)制,定期驗(yàn)證代理池中ip的有效性,并刪除掉所有失效的ip
3.一個(gè)web服務(wù),用以提供獲取一個(gè)隨機(jī)代理的api
4.squid3的維持腳本,它定期獲取代理池中的可用ip,更新squid中的可轉(zhuǎn)發(fā)代理列表
5.一個(gè)調(diào)度器,程序的入口,用來協(xié)調(diào)各組件的運(yùn)行
如果不是很理解,沒關(guān)系,請往下看,我會(huì)細(xì)說。
環(huán)境說明
為了實(shí)現(xiàn)代理IP池,我們?nèi)缦碌能浖h(huán)境(列舉主要部分):
1.redis服務(wù)器,用以存放代理池相關(guān)數(shù)據(jù)
2.flask,用以實(shí)現(xiàn)提取單個(gè)隨機(jī)代理的api
3.squid3,用以實(shí)現(xiàn)代理轉(zhuǎn)發(fā)
組件1-獲取代理ip的渠道
我們有很多種渠道獲取代理ip?;\統(tǒng)一點(diǎn)來說,可以分為兩類,免費(fèi)代理和收費(fèi)代理。
免費(fèi)代理,顧名思義嘛,最大的優(yōu)點(diǎn)就是免費(fèi),不需要什么成本,網(wǎng)上搜一下就能找到。缺點(diǎn)也很明顯,免費(fèi)代理畢竟是免費(fèi)的,所以質(zhì)量根本不能保證,大部分無法使用,能用的多數(shù)也速度奇慢。
收費(fèi)代理的質(zhì)量相對來說就好多了,不同平臺(tái)的代理質(zhì)量和價(jià)格上都有些出入,可以自行比較。
個(gè)人學(xué)習(xí)的話,如果真的資金優(yōu)先,可以考慮采集免費(fèi)代理;如果資金相對充裕,可以花錢買一天或一周的代理使用,價(jià)格也不貴。我是比較推薦收費(fèi)代理的,因?yàn)槊赓M(fèi)代理的質(zhì)量真的不敢恭維。
企業(yè)商用的話,優(yōu)先考慮收費(fèi)代理吧,會(huì)穩(wěn)定很多。
我選擇的代理服務(wù)商是站大爺(http://ip.zdaye.com/),聲明一下,我真的沒收廣告費(fèi)啊= =。坦言說,站大爺?shù)拇碣|(zhì)量只能算一般,不過也夠用。有幾家的質(zhì)量比它要好一些,不過好的有限。讓我選擇站大爺?shù)淖畲笤蚴?#xff0c;它支持賬號(hào)密碼訪問的模式。
沒用過收費(fèi)代理的朋友可能不清楚,使用收費(fèi)代理平臺(tái)的接口,從平臺(tái)上批量提取代理ip或使用代理時(shí),一般都是要綁定你的機(jī)器ip的。比如,你的機(jī)器ip是123.123.123.123,你就需要事先在平臺(tái)上把ip綁定為123.123.123.123,這樣,你只能通過IP為123.123.123.123的機(jī)器從平臺(tái)提取ip,提取出的ip也只能由ip為123.123.123.123的機(jī)器使用,其他ip的機(jī)器都不行。當(dāng)我們有多臺(tái)機(jī)器的時(shí)候,就會(huì)非常尷尬了,畢竟不能給每臺(tái)機(jī)器都買一次代理吧,很不劃算。
在站大爺上面,除了綁定ip這個(gè)方法外,還可以選擇使用賬號(hào)+密碼提取/使用代理,選擇這個(gè)方法的話就不再收到IP地址的限制。講道理,有點(diǎn)舒服啊= =
我先面的編碼以站大爺為例,使用其他代理服務(wù)的可自行編寫相關(guān)腳本,原理和邏輯都是相通的,部分細(xì)節(jié)上針對處理即可。
購買的細(xì)節(jié)我也不說了,如果需要購買的話,直接去官網(wǎng)購入短效優(yōu)質(zhì)代理即可。
先放出這部分的完整代碼,附有注釋。
-- coding: utf-8 --
@File : get_ip.py
@Author: AaronJny
@Date : 18-12-14 上午10:44
@Desc : 從指定網(wǎng)站上獲取代理ip,
我目前在使用站大爺,就以站大爺為例
import requests
import time
import utils
import settings
from gevent.pool import Pool
from gevent import monkey
monkey.patch_all()
class ZdyIpGetter:
“”" 從站大爺上提取代理ip的腳本,使用其他代理服務(wù)的可自行編寫相關(guān)腳本, 原理和邏輯都是相通的,部分細(xì)節(jié)上需要針對處理 “”"
def init(self):
# 購買服務(wù)時(shí),網(wǎng)站給出的提取ip的api,替換成自己的
self.api_url = ‘http://xxxxxxxxxxxxxxxxxxxxxxxxxx’
self.logger = utils.get_logger(getattr(self.class, ‘name’))
self.proxy_list = []
self.good_proxy_list = []
self.pool = Pool(5)
self.server = utils.get_redis_client()
def check_proxy(self, proxy):
“”" 檢查代理是否可用, 并將可用代理加入到指定列表中 :param proxy: :return: “”"
if settings.USE_PASSWORD:
tmp_proxy = ‘{}:{}@{}’.format(settings.USERNAME, settings.PASSWORD, proxy)
else:
tmp_proxy = ‘{}’.format(proxy)
proxies = {
‘http’: ‘http://’ + tmp_proxy,
‘https’: ‘https://’ + tmp_proxy,
}
try:
# 驗(yàn)證代理是否可用時(shí),訪問的是ip138的服務(wù)
resp = requests.get(‘http://2019.ip138.com/ic.asp’, proxies=proxies, timeout=10)
# self.logger.info(resp.content.decode(‘gb2312’))
# 判斷是否成功使用代理ip進(jìn)行訪問
assert proxy.split(‘:’)[0] in resp.content.decode(‘gb2312’)
self.logger.info(‘[GOOD] - {}’.format(proxy))
self.good_proxy_list.append(proxy)
except Exception as e:
self.logger.info(‘[BAD] - {} , {}’.format(proxy, e.args))
def get_proxy_list(self):
“”" 提取一批ip,篩選出可用的部分 注:當(dāng)可用ip小于兩個(gè)時(shí),則保留全部ip(不論測試成功與否) :return: “”"
while True:
try:
res = requests.get(self.api_url, timeout=10).content.decode(‘utf8’)
break
except Exception as e:
self.logger.error(‘獲取代理列表失敗!重試!{}’.format(e))
time.sleep(1)
if len(res) == 0:
self.logger.error(‘未獲取到數(shù)據(jù)!’)
elif ‘bad’ in res:
self.logger.error(‘請求失敗!’)
# 檢測未考慮到的異常情況
elif res.count(‘.’) != 15:
self.logger.error(res)
else:
self.logger.info(‘開始讀取代理列表!’)
for line in res.split():
if ‘:’ in line:
self.proxy_list.append(line.strip())
self.pool.map(self.check_proxy, self.proxy_list)
self.pool.join()
# 當(dāng)本次檢測可用代理數(shù)量小于2個(gè)時(shí),則認(rèn)為檢測失敗,代理全部可用
if len(self.good_proxy_list) < 2:
self.good_proxy_list = self.proxy_list.copy()
self.logger.info(‘>>>> 完成! <<<<’)
def save_to_redis(self):
“”" 將提取到的有效ip保存到redis中, 供其他組件訪問 :return: “”"
for proxy in self.good_proxy_list:
self.server.zadd(settings.IP_POOL_KEY, int(time.time()) + settings.PROXY_IP_TTL, proxy)
def fetch_new_ip(self):
“”" 獲取一次新ip的整體流程控制 :return: “”"
self.proxy_list.clear()
self.good_proxy_list.clear()
self.get_proxy_list()
self.save_to_redis()
def main(self):
“”" 周期獲取新ip :return: “”"
start = time.time()
while True:
# 每 settings.FETCH_INTERVAL 秒獲取一批新IP
if time.time() - start >= settings.FETCH_INTERVAL:
self.fetch_new_ip()
start = time.time()
time.sleep(2)
if name == ‘main’:
ZdyIpGetter().main()
說一下這里面的關(guān)鍵部分:
1.如何保存代理池相關(guān)數(shù)據(jù)?
從平臺(tái)上獲取的ip是有生命周期的,一般幾分鐘后就會(huì)失效,所以我們需要用類似于字典的形式保存代理IP和它的過期時(shí)間
為了更好地容錯(cuò),我們將從平臺(tái)上提取到的ip的生命周期統(tǒng)一設(shè)置為settings.PROXY_IP_TTL,而代理的可用時(shí)間一般是大于settings.PROXY_IP_TTL(我默認(rèn)設(shè)置的是60s)。
為了保證處理效率,實(shí)際使用的redis數(shù)據(jù)結(jié)構(gòu)并非散列表(類似于python中的字典),而是zset(有序集合,可以為集合里面的每個(gè)元素設(shè)置一個(gè)分?jǐn)?shù),并能夠分?jǐn)?shù)來篩選區(qū)間內(nèi)的元素)。這里,代理ip是zset中的元素,過期時(shí)間是元素的分?jǐn)?shù),參考上面的save_to_redis(self)代碼。
2.如何驗(yàn)證提取到的ip是否可用?
提取到的ip有些可能是不能用的,所以我先進(jìn)行了驗(yàn)證,再將有效的加入到代理池中
使用ip138的接口驗(yàn)證代理是否生效
校驗(yàn)之后,如果可用的ip非常少或全部失敗,我傾向于認(rèn)為是檢驗(yàn)手段出了問題,并認(rèn)為此批ip均為正常的,加入到代理池中
組件2-檢驗(yàn)并清理過期ip
因?yàn)槲医o每個(gè)加入代理池的ip都設(shè)置了過期時(shí)間,所以檢查代理ip是否有效這個(gè)操作,也并非真的去檢驗(yàn)ip本身,而是檢查它的過期時(shí)間。
我們需要清除掉過期時(shí)間<當(dāng)前時(shí)間的ip,而zset可以快速實(shí)現(xiàn)此操作。
-- coding: utf-8 --
@File : delele_ip.py
@Author: AaronJny
@Date : 18-12-14 上午11:15
@Desc : 過期ip清理器
import utils
import settings
import time
class ExpireIpCleaner:
def init(self):
self.logger = utils.get_logger(getattr(self.class, ‘name’))
self.server = utils.get_redis_client()
def clean(self):
“”" 清理代理池中的過期ip :return: “”"
self.logger.info(‘開始清理過期ip’)
# 計(jì)算清理前代理池的大小
total_before = int(self.server.zcard(settings.IP_POOL_KEY))
# 清理
self.server.zremrangebyscore(settings.IP_POOL_KEY, 0, int(time.time()))
# 計(jì)算清理后代理池的大小
total_after = int(self.server.zcard(settings.IP_POOL_KEY))
self.logger.info(‘完畢!清理前可用ip {},清理后可用ip {}’.format(total_before, total_after))
def main(self):
“”" 周期性的清理過期ip :return: “”"
while True:
self.clean()
self.logger.info(‘*’ * 40)
time.sleep(settings.CLEAN_INTERVAL)
if name == ‘main’:
ExpireIpCleaner().main()
定期進(jìn)行檢測和清理,很簡單,沒有什么需要說的。
組件3-獲取隨機(jī)ip的web接口
不得不說,使用flask開發(fā)簡單的接口真的是太舒服了,簡潔而快速。這個(gè)web服務(wù)提供兩個(gè)小功能:
1.獲取一個(gè)隨機(jī)的可用代理ip
2.查看當(dāng)前代理池中可用的代理ip的數(shù)量
– coding: utf-8 –
@File : web_api.py
@Author: AaronJny
@Date : 18-12-14 上午11:22
@Desc : 提供http接口的web程序
import utils
import settings
import flask
import random
import time
redis_client = utils.get_redis_client()
ip_pool_key = settings.IP_POOL_KEY
app = flask.Flask(name)
@app.route(‘/random/’)
def random_ip():
“”" 獲取一個(gè)隨機(jī)ip :return: “”"
# 獲取redis中仍可用的全部ip
proxy_ips = redis_client.zrangebyscore(ip_pool_key, int(time.time()),
int(time.time()) + settings.PROXY_IP_TTL * 10)
if proxy_ips:
ip = random.choice(proxy_ips)
# 如果ip需要密碼訪問,則添加
if settings.USE_PASSWORD:
ip = ‘{}:{}@{}’.format(settings.USERNAME, settings.PASSWORD, ip.decode(‘utf8’))
return ip
else:
return ‘’
@app.route(‘/total/’)
def total_ip():
“”" 統(tǒng)計(jì)池中可用代理的數(shù)量 :return: “”"
total = redis_client.zcard(ip_pool_key)
if total:
return str(total)
else:
return ‘0’
def main():
“”" 程序運(yùn)行入口 :return: “”"
app.run(‘0.0.0.0’, port=settings.API_WEB_PORT)
if name == ‘main’:
app.run(‘0.0.0.0’, port=settings.API_WEB_PORT)
都很簡單,就不細(xì)說了。
組件4-squid的維持、更新腳本
處理http的接口外,我們還可以使用squid做代理轉(zhuǎn)發(fā),這樣,在爬蟲程序中就不需要再頻繁地更換代理IP地址,直接填上squid的地址,它會(huì)自動(dòng)幫你轉(zhuǎn)發(fā)給其他代理ip。
這個(gè)腳本提供如下功能:
1.從代理池中讀取所有可用代理ip,作為可轉(zhuǎn)發(fā)的代理列表寫入到squid的配置文件中,并通過命令使squid重新加載配置文件。這樣,squid一直使用最新可用的那些代理ip。
2.當(dāng)squid服務(wù)異常時(shí),通過命令殺死所有squid進(jìn)程,并重新開啟,保證服務(wù)正常運(yùn)行
下面的代碼中使用了名為squid.conf的文件,此文件在github上,是關(guān)于squid的一些配置。如果需要對squid進(jìn)行深度定制,需要自行修改這個(gè)文件。
-- coding: utf-8 --
@File : squid_keeper.py
@Author: AaronJny
@Date : 18-12-14 上午11:27
@Desc : 維持squid3使用可用ip的腳本
import utils
import settings
import time
import os
import subprocess
class SquidKeeper:
def init(self):
self.logger = utils.get_logger(getattr(self.class, ‘name’))
self.server = utils.get_redis_client()
self.ip_pool_key = settings.IP_POOL_KEY
# 區(qū)別對待使用密碼和不使用密碼的配置模板
if settings.USE_PASSWORD:
self.peer_conf = “cache_peer %s parent %s 0 no-query proxy-only login={}:{} never_direct allow all round-robin weight=1 connect-fail-limit=2 allow-miss max-conn=5\n”.format(
settings.USERNAME, settings.PASSWORD)
else:
self.peer_conf = “cache_peer %s parent %s 0 no-query proxy-only never_direct allow all round-robin weight=1 connect-fail-limit=2 allow-miss max-conn=5\n”
def read_new_ip(self):
“”" 從redis中讀取全部有效ip :return: “”"
self.logger.info(‘讀取代理池中可用ip’)
proxy_ips = self.server.zrangebyscore(settings.IP_POOL_KEY, int(time.time()),
int(time.time()) + settings.PROXY_IP_TTL * 10)
return proxy_ips
def update_conf(self, proxy_list):
“”" 根據(jù)讀取到的代理ip,和現(xiàn)有配置文件模板, 生成新的squid配置文件并重新加載,讓squid使用最新的ip。 :param proxy_list: :return: “”"
self.logger.info(‘準(zhǔn)備加載到squid中’)
with open(‘squid.conf’, ‘r’) as f:
squid_conf = f.readlines()
squid_conf.append(‘\n# Cache peer config\n’)
for proxy in proxy_list:
ip, port = proxy.decode(‘utf8’).split(‘:’)
squid_conf.append(self.peer_conf % (ip, port))
with open(‘/etc/squid/squid.conf’, ‘w’) as f:
f.writelines(squid_conf)
failed = os.system(‘squid -k reconfigure’)
# 這是一個(gè)容錯(cuò)措施
# 當(dāng)重新加載配置文件失敗時(shí),會(huì)殺死全部相關(guān)進(jìn)行并重試
if failed:
self.logger.info(‘squid進(jìn)程出現(xiàn)問題,查找當(dāng)前啟動(dòng)的squid相關(guān)進(jìn)程…’)
p = subprocess.Popen(“ps -ef | grep squid | grep -v grep | awk ‘{print $2}’”, shell=True,
stdout=subprocess.PIPE, universal_newlines=True)
p.wait()
result_lines = [int(x.strip()) for x in p.stdout.readlines()]
self.logger.info(‘找到如下進(jìn)程:{}’.format(result_lines))
if len(result_lines):
for proc_id in result_lines:
self.logger.info(‘開始?xì)⑺肋M(jìn)程 {}…’.format(proc_id))
os.system(‘kill -s 9 {}’.format(proc_id))
self.logger.info(‘全部squid已被殺死,開啟新squid進(jìn)程…’)
os.system(‘service squid restart’)
time.sleep(3)
self.logger.info(‘重新加載ip…’)
os.system(‘squid -k reconfigure’)
self.logger.info(‘當(dāng)前可用IP數(shù)量 {}’.format(len(proxy_list)))
def main(self):
“”" 周期性地更新squid的配置文件, 使其使用最新的代理ip :return: “”"
while True:
proxy_list = self.read_new_ip()
self.update_conf(proxy_list)
self.logger.info(‘*’ * 40)
time.sleep(settings.SQUID_KEEPER_INTERVAL)
if name == ‘main’:
SquidKeeper().main()
組件5-調(diào)度器
調(diào)度器是程序的入口,也是對以上各個(gè)組件的控制和整合。
它的主要功能是:
1.使用子進(jìn)程分別開啟各個(gè)組件
2.在某個(gè)組件異常退出后,重啟它
3.接收到終止信號(hào)時(shí),關(guān)閉所有存活的組件進(jìn)程后再退出
– coding: utf-8 –
@File : scheduler.py
@Author: AaronJny
@Date : 18-12-14 上午11:41
@Desc : 調(diào)度中心,所有組件在這里被統(tǒng)一啟動(dòng)和調(diào)度
import utils
import settings
from get_ip import ZdyIpGetter
from delele_ip import ExpireIpCleaner
from web_api import app
from squid_keeper import SquidKeeper
from multiprocessing import Process
import time
class Scheduler:
logger = utils.get_logger(‘Scheduler’)
@staticmethod
def fetch_ip():
“”"
獲取新ip的進(jìn)程
:return:
“”"
while True:
try:
ZdyIpGetter().main()
except Exception as e:
print(e.args)
@staticmethod
def clean_ip():
“”"
定期清理過期ip的進(jìn)程
:return:
“”"
while True:
try:
ExpireIpCleaner().main()
except Exception as e:
print(e.args)
@staticmethod
def squid_keep():
“”"
維持squid使用最新ip的進(jìn)程
:return:
“”"
while True:
try:
SquidKeeper().main()
except Exception as e:
print(e.args)
@staticmethod
def api():
“”"
提供web接口的進(jìn)程
:return:
“”"
app.run(‘0.0.0.0’, settings.API_WEB_PORT)
def run(self):
process_list = []
try:
# 只啟動(dòng)打開了開關(guān)的組件
if settings.IP_GETTER_OPENED:
# 創(chuàng)建進(jìn)程對象
fetch_ip_process = Process(target=Scheduler.fetch_ip)
# 并將組件進(jìn)程加入到列表中,方便在手動(dòng)退出的時(shí)候殺死
process_list.append(fetch_ip_process)
# 開啟進(jìn)程
fetch_ip_process.start()
if settings.EXPIRE_IP_CLEANER_OPENED:
clean_ip_process = Process(target=Scheduler.clean_ip)
process_list.append(clean_ip_process)
clean_ip_process.start()
if settings.SQUID_KEEPER_OPENED:
squid_keep_process = Process(target=Scheduler.squid_keep)
process_list.append(squid_keep_process)
squid_keep_process.start()
if settings.WEB_API_OPENED:
api_process = Process(target=Scheduler.api)
process_list.append(api_process)
api_process.start()
# 一直執(zhí)行,直到收到終止信號(hào)
while True:
time.sleep(1)
except KeyboardInterrupt:
# 收到終止信號(hào)時(shí),關(guān)閉所有進(jìn)程后再退出
self.logger.info(‘收到終止信號(hào),正在關(guān)閉所有進(jìn)程…’)
for process in process_list:
if process.is_alive():
process.terminate()
self.logger.info(‘關(guān)閉完成!結(jié)束程序!’)
if name == ‘main’:
Scheduler().run()
公用方法和配置
將各組件公用的方法和配置抽取出來,做了集中。
-- coding: utf-8 --
@File : utils.py
@Author: AaronJny
@Date : 18-12-14 上午11:07
@Desc :
from redis import StrictRedis, ConnectionPool
import settings
import logging
def get_redis_client():
“”" 獲取一個(gè)redis連接 :return: “”"
server_url = settings.REDIS_SERVER_URL
return StrictRedis(connection_pool=ConnectionPool.from_url(server_url))
def get_logger(name=name):
“”" 獲取一個(gè)logger,用以格式化輸出信息 :param name: :return: “”"
logger = logging.getLogger(name)
logger.handlers.clear()
logger.setLevel(logging.INFO)
formatter = logging.Formatter(
‘%(asctime)s - %(name)s - %(levelname)s: - %(message)s’,
datefmt=‘%Y-%m-%d %H:%M:%S’)
# 使用StreamHandler輸出到屏幕
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
ch.setFormatter(formatter)
logger.addHandler(ch)
return logger
涉及到的所有配置,可以根據(jù)情況進(jìn)行修改:
-- coding: utf-8 --
@File : settings.py
@Author: AaronJny
@Date : 18-12-14 上午11:13
@Desc :
代理池redis鍵名
IP_POOL_KEY = ‘open_proxy_pool’
redis連接,根據(jù)實(shí)際情況進(jìn)行配置
REDIS_SERVER_URL = ‘redis://:your_password@your_host:port/db_name’
api對外端口
API_WEB_PORT = 9102
代理是否需要通過密碼訪問,當(dāng)此項(xiàng)為False時(shí)可無視USERNAME和PASSWORD的配置
USE_PASSWORD = True
用戶名
注意:用戶名密碼是指代理服務(wù)方提供給你,用以驗(yàn)證訪問授權(quán)的憑證。
無密碼限制時(shí)可無視此項(xiàng),并將USE_PASSWORD改為False
USERNAME = ‘your_username’
密碼
PASSWORD = ‘your_password’
功能組件開關(guān)*
打開web api功能,不使用web api的話可以關(guān)閉
WEB_API_OPENED = True
打開squid代理轉(zhuǎn)發(fā)服務(wù)的維持腳本,不使用squid的話可以關(guān)閉
SQUID_KEEPER_OPENED = True
打開清理過期ip的腳本,如果池內(nèi)的代理ip永遠(yuǎn)不會(huì)失效的話可以關(guān)閉
EXPIRE_IP_CLEANER_OPENED = True
打開定時(shí)獲取ip并檢查的腳本,如果不需要獲取新ip的話可以關(guān)閉
IP_GETTER_OPENED = True
***********************************
清理代理ip的頻率,如下配置代表每兩次之間間隔6秒
CLEAN_INTERVAL = 6
獲取代理ip的頻率,根據(jù)api的請求頻率限制進(jìn)行設(shè)置
比如站大爺?shù)念l率限制是10秒一次,我就設(shè)置成了12秒
FETCH_INTERVAL = 12
squid從redis中加載新ip的頻率
SQUID_KEEPER_INTERVAL = 12
代理ip的生命周期,即一個(gè)新ip在多久后將被刪除,單位:秒
PROXY_IP_TTL = 60
運(yùn)行
到這里,編碼就完成了。打開終端,切換到項(xiàng)目根目錄,輸入python3 scheduler.py運(yùn)行即可。建議使用screen后臺(tái)運(yùn)行。
給出一個(gè)運(yùn)行的截圖(有機(jī)器在調(diào)用接口,我把ip隱藏了):
總結(jié)
以上是生活随笔為你收集整理的如何构建一个自己的代理ip池的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 唯唯码 - ios/android的ap
- 下一篇: ORA-28000: the accou