Source code for proxypool.schedule
"""
-------------------------------------------------
File Name: schedule.py
Description: 调度器模块,
包含ValidityTester,PoolAdder,
Schedule三个类,负责维护代理池。
Author: Liu
Date: 2016/12/9
-------------------------------------------------
"""
import time
from multiprocessing import Process
import asyncio
import aiohttp
from .db import RedisClient
from .error import ResourceDepletionError
from .proxyGetter import FreeProxyGetter
from .setting import *
[docs]class ValidityTester(object):
"""
检验器,负责对未知的代理进行异步检测。
"""
# 用百度的首页来检验
test_api = 'https://www.baidu.com'
def __init__(self):
self._raw_proxies = None
self._usable_proxies = []
[docs] def set_raw_proxies(self, proxies):
"""设置待检测的代理。
"""
self._raw_proxies = proxies
self._usable_proxies = []
[docs] async def test_single_proxy(self, proxy):
"""检测单个代理,如果可用,则将其加入_usable_proxies
"""
async with aiohttp.ClientSession() as session:
try:
real_proxy = 'http://' + proxy
async with session.get(self.test_api, proxy=real_proxy, timeout=15) as resp:
self._usable_proxies.append(proxy)
except Exception:
pass
[docs] def test(self):
"""异步检测_raw_proxies中的全部代理。
"""
print('ValidityTester is working')
loop = asyncio.get_event_loop()
tasks = [self.test_single_proxy(proxy) for proxy in self._raw_proxies]
loop.run_until_complete(asyncio.wait(tasks))
[docs] def get_usable_proxies(self):
return self._usable_proxies
[docs]class PoolAdder(object):
"""
添加器,负责向池中补充代理
"""
def __init__(self, threshold):
self._threshold = threshold
self._conn = RedisClient()
self._tester = ValidityTester()
self._crawler = FreeProxyGetter()
[docs] def is_over_threshold(self):
"""
判断代理池中的数据量是否达到阈值。
"""
if self._conn.queue_len >= self._threshold:
return True
else:
return False
[docs] def add_to_queue(self, flag=40):
"""
命令爬虫抓取一定量未检测的代理,然后检测,将通过检测的代理
加入到代理池中。
"""
print('PoolAdder is working')
while not self.is_over_threshold():
for callback_label in range(self._crawler.__CrawlFuncCount__):
callback = self._crawler.__CrawlFunc__[callback_label]
raw_proxies = self._crawler.get_raw_proxies(callback, flag)
self._tester.set_raw_proxies(raw_proxies)
self._tester.test()
self._conn.put_many(self._tester.get_usable_proxies())
if self.is_over_threshold():
break
flag += flag
if flag >= 10 * flag:
raise ResourceDepletionError
[docs]class Schedule(object):
"""
总调度器,用于协调各调度器模块
"""
@staticmethod
[docs] def valid_proxy(cycle=VALID_CHECK_CYCLE):
"""
对已经如池的代理进行检测,防止池中的代理因长期
不使用而过期。
抽出代理池队列中前1/4的代理,检测,合格者压入队列尾。
"""
conn = RedisClient()
tester = ValidityTester()
while True:
time.sleep(cycle)
count = int(0.25 * conn.queue_len)
if count == 0:
continue
raw_proxies = conn.get(count)
tester.set_raw_proxies(raw_proxies)
tester.test()
proxies = tester.get_usable_proxies()
conn.put_many(proxies)
@staticmethod
[docs] def check_pool(lower_threshold=POOL_LOWER_THRESHOLD,
upper_threshold=POOL_UPPER_THRESHOLD,
cycle=POOL_LEN_CHECK_CYCLE):
"""
协调添加器,当代理池中可用代理的数量低于下阈值时,触发添加器,启动爬虫
补充代理,当代理达到上阈值时,添加器停止工作。
"""
conn = RedisClient()
adder = PoolAdder(upper_threshold)
while True:
if conn.queue_len < lower_threshold:
adder.add_to_queue()
time.sleep(cycle)
[docs] def run(self):
"""
运行调度器,创建两个进程,对代理池进行维护。
"""
valid_process = Process(target=Schedule.valid_proxy)
check_process = Process(target=Schedule.check_pool)
valid_process.start()
check_process.start()