通过Flask和Redis构造一个动态维护的代理池
代理池的維護(hù)
目前有很多網(wǎng)站提供免費(fèi)代理,而且種類齊全,比如各個(gè)地區(qū)、各個(gè)匿名級(jí)別的都有,不過(guò)質(zhì)量實(shí)在不敢恭維,畢竟都是免費(fèi)公開(kāi)的,可能一個(gè)代理無(wú)數(shù)個(gè)人在用也說(shuō)不定。所以我們需要做的是大量抓取這些免費(fèi)代理,然后篩選出其中可用的代理存儲(chǔ)起來(lái)供我們使用,不可用的進(jìn)行剔除。
?
獲取代理的途徑
?維護(hù)一個(gè)代理池第一步就是要找到提供免費(fèi)代理的站點(diǎn),例如PROXY360,網(wǎng)頁(yè)內(nèi)容如下:
?
?
而且可以看到網(wǎng)頁(yè)里提供了一些免費(fèi)代理列表,包括服務(wù)器地址、端口、代理種類、地區(qū)、更新時(shí)間等等信息。
當(dāng)前我們需要的就是代理服務(wù)器和端口信息,將其爬取下來(lái)即可。
維護(hù)代理
那么問(wèn)題來(lái)了,當(dāng)我們把這些免費(fèi)的代理爬取下來(lái)后,該怎么去保存他們?
首先我們需要確保這個(gè)數(shù)據(jù)庫(kù)可以是我們邊存邊取,另外還需要定時(shí)檢查隊(duì)列中那些過(guò)期的代理并將它們剔除,所以需要易于存取。
另外怎么區(qū)分代理的新舊,如果按照代理網(wǎng)站上給出的代理的修改時(shí)間來(lái)標(biāo)識(shí)是可行的,但是更簡(jiǎn)單的方法就是維護(hù)一個(gè)隊(duì)列,確保只從一端存入,例如右端,這樣就能確保最新的代理一直處于隊(duì)列的右段,而在隊(duì)列左端的則是存取時(shí)間較長(zhǎng)的,那么在使用時(shí)我們就可以提取隊(duì)列右端的代理來(lái)使用。
那么對(duì)于隊(duì)列左端的代理我們也不能就這么任其老化,還要做的操作就是把左端的代理提取出來(lái),進(jìn)行測(cè)試,將那些可以使用的放入隊(duì)列右端,不能使用了的則剔除隊(duì)列。
通過(guò)以上操作,就保證了隊(duì)列里的代理一直處于可用狀態(tài)。
所以從目前來(lái)看,既能高效處理,又可以做到隊(duì)列動(dòng)態(tài)維護(hù),合適的方法就是使用Redis數(shù)據(jù)庫(kù)的隊(duì)列。
?
下面這張流程圖就是整個(gè)代理隊(duì)列需要具備的功能:
?
可以定義一個(gè)類來(lái)維護(hù)一個(gè)Redis隊(duì)列,比如get方法是從左端取出,put方法是從右端放入,pop方法時(shí)從右端取出可用代理。
?
import redisfrom proxypool.error import PoolEmptyError
from proxypool.setting import HOST,PORT,PASSWORD
class RedisClient(object):
def __init__(self,host=HOST,port=PORT,password=PASSWORD):
if password:
self._db = redis.Redis(host,port,password)
else:
self._db = redis.Redis(host,port)
def get(self, count=1):
proxies = self._db.Irange("proxies",0, count - 1)
self._db.Itrim("proxis", count, -1)
return proxies
def put(self,proxy):
self._db.rpush("proxies", proxy)
def pop(self,self):
try:
self._db.rpop("proxies").decode('utf-8')
except:
raise PoolEmptyError
?
?
?
檢測(cè)代理
那么如何來(lái)檢測(cè)代理是否可用呢?可以使用這個(gè)代理來(lái)請(qǐng)求某個(gè)網(wǎng)站,如果返回的200則證明這個(gè)代理可用,否則代理不可使用。
?
conn = RedisClient() proxies = {'http': proxy} r = requests.get(url,proxies=proxies) if r.status_code == 200:conn.put(proxy)
例如在這里proxy就是要檢測(cè)的代理,使用requests庫(kù)設(shè)置好這個(gè)代理,然后請(qǐng)求百度,正常請(qǐng)求,那就可以將這個(gè)代理存入Redis。
?
?
獲取可用代理
現(xiàn)在我們維護(hù)了一個(gè)代理池,那么這個(gè)代理池需要是可以公用的。
比如現(xiàn)在有多個(gè)爬蟲(chóng)項(xiàng)目都需要用到代理,而代理池的維護(hù)作為另外的一個(gè)項(xiàng)目,他們之間如果要建立連接,最恰當(dāng)?shù)姆绞骄褪墙涌凇?/span>
所以可以利用Web服務(wù)器來(lái)實(shí)現(xiàn)一個(gè)接口,其他的項(xiàng)目通過(guò)請(qǐng)求這個(gè)接口得到內(nèi)容獲取到一個(gè)可用代理,這樣保證了代理池的通用性。
所以要實(shí)現(xiàn)這個(gè)還需要一個(gè)Web服務(wù)器,例如Flask,Tornado等等。
例如使用Flask,定義一個(gè)路由,然后調(diào)用的RedisClient的pop方法,返回結(jié)果即可。
?
@app.route('/get') def get_proxy():conn = RedisClient() return conn.pop()
這樣一來(lái),整個(gè)程序運(yùn)行起來(lái)后,請(qǐng)求網(wǎng)頁(yè)就可以看到一個(gè)可用代理了。
?
?
使用代理
使用代理只需要請(qǐng)求這個(gè)站點(diǎn),就可以拿到使用的代理了。
?
def get_proxy():r = requests.get('http://127.0.0.1:5000/get')proxy = r.textreturn proxydef crawl(url, proxy):proxies = {'http': get_proxy()}r = requests.get(url, proxies=proxies)#do something?
?
樣例實(shí)現(xiàn) ?
?
https://github.com/gzf1234/ProxyPool
?
轉(zhuǎn)載于:https://www.cnblogs.com/coder-gao/p/7559801.html
總結(jié)
以上是生活随笔為你收集整理的通过Flask和Redis构造一个动态维护的代理池的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Wecoach陈隽永:如何打造一款完美的
- 下一篇: SQL查询交集、并集、差集