schedule.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. """
  2. -------------------------------------------------
  3. File Name: schedule.py
  4. Description: 调度器模块,
  5. 包含ValidityTester,PoolAdder,
  6. Schedule三个类,负责维护代理池。
  7. Author: Liu
  8. Date: 2016/12/9
  9. -------------------------------------------------
  10. """
  11. import time
  12. from multiprocessing import Process
  13. import asyncio
  14. import aiohttp
  15. from .db import RedisClient
  16. from .error import ResourceDepletionError
  17. from .getter import FreeProxyGetter
  18. from .setting import *
  19. class ValidityTester(object):
  20. """
  21. 检验器,负责对未知的代理进行异步检测。
  22. """
  23. # 用百度的首页来检验
  24. test_api = 'http://www.baidu.com'
  25. def __init__(self):
  26. self._raw_proxies = None
  27. self._usable_proxies = []
  28. def set_raw_proxies(self, proxies):
  29. """设置待检测的代理。
  30. """
  31. self._raw_proxies = proxies
  32. self._usable_proxies = []
  33. async def test_single_proxy(self, proxy):
  34. """检测单个代理,如果可用,则将其加入_usable_proxies
  35. """
  36. async with aiohttp.ClientSession() as session:
  37. try:
  38. real_proxy = 'http://' + proxy
  39. print('Testing', real_proxy)
  40. async with session.get(self.test_api, proxy=real_proxy, timeout=15) as response:
  41. await response
  42. print('Response from', proxy)
  43. self._usable_proxies.append(proxy)
  44. except Exception:
  45. pass
  46. def test(self):
  47. """异步检测_raw_proxies中的全部代理。
  48. """
  49. print('ValidityTester is working')
  50. loop = asyncio.get_event_loop()
  51. tasks = [self.test_single_proxy(proxy) for proxy in self._raw_proxies]
  52. loop.run_until_complete(asyncio.wait(tasks))
  53. def get_usable_proxies(self):
  54. return self._usable_proxies
  55. class PoolAdder(object):
  56. """
  57. 添加器,负责向池中补充代理
  58. """
  59. def __init__(self, threshold):
  60. self._threshold = threshold
  61. self._conn = RedisClient()
  62. self._tester = ValidityTester()
  63. self._crawler = FreeProxyGetter()
  64. def is_over_threshold(self):
  65. """
  66. 判断代理池中的数据量是否达到阈值。
  67. """
  68. if self._conn.queue_len >= self._threshold:
  69. return True
  70. else:
  71. return False
  72. def add_to_queue(self):
  73. """
  74. 命令爬虫抓取一定量未检测的代理,然后检测,将通过检测的代理
  75. 加入到代理池中。
  76. """
  77. print('PoolAdder is working')
  78. proxy_count = 0
  79. if not self.is_over_threshold():
  80. for callback_label in range(self._crawler.__CrawlFuncCount__):
  81. callback = self._crawler.__CrawlFunc__[callback_label]
  82. raw_proxies = self._crawler.get_raw_proxies(callback)
  83. self._tester.set_raw_proxies(raw_proxies)
  84. self._tester.test()
  85. self._conn.put_many(self._tester.get_usable_proxies())
  86. proxy_count += len(raw_proxies)
  87. if proxy_count == 0:
  88. raise ResourceDepletionError
  89. class Schedule(object):
  90. """
  91. 总调度器,用于协调各调度器模块
  92. """
  93. @staticmethod
  94. def valid_proxy(cycle=VALID_CHECK_CYCLE):
  95. """
  96. 对已经如池的代理进行检测,防止池中的代理因长期
  97. 不使用而过期。
  98. 抽出代理池队列中前1/4的代理,检测,合格者压入队列尾。
  99. """
  100. conn = RedisClient()
  101. tester = ValidityTester()
  102. while True:
  103. time.sleep(cycle)
  104. count = int(0.25 * conn.queue_len)
  105. if count == 0:
  106. continue
  107. raw_proxies = conn.get(count)
  108. tester.set_raw_proxies(raw_proxies)
  109. tester.test()
  110. proxies = tester.get_usable_proxies()
  111. conn.put_many(proxies)
  112. @staticmethod
  113. def check_pool(lower_threshold=POOL_LOWER_THRESHOLD,
  114. upper_threshold=POOL_UPPER_THRESHOLD,
  115. cycle=POOL_LEN_CHECK_CYCLE):
  116. """
  117. 协调添加器,当代理池中可用代理的数量低于下阈值时,触发添加器,启动爬虫
  118. 补充代理,当代理达到上阈值时,添加器停止工作。
  119. """
  120. conn = RedisClient()
  121. adder = PoolAdder(upper_threshold)
  122. while True:
  123. if conn.queue_len < lower_threshold:
  124. adder.add_to_queue()
  125. time.sleep(cycle)
  126. def run(self):
  127. """
  128. 运行调度器,创建两个进程,对代理池进行维护。
  129. """
  130. valid_process = Process(target=Schedule.valid_proxy)
  131. check_process = Process(target=Schedule.check_pool)
  132. valid_process.start()
  133. check_process.start()