schedule.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import time
  2. from multiprocessing import Process
  3. import asyncio
  4. import aiohttp
  5. from aiohttp.errors import ProxyConnectionError
  6. from proxypool.db import RedisClient
  7. from proxypool.error import ResourceDepletionError
  8. from proxypool.getter import FreeProxyGetter
  9. from proxypool.setting import *
  10. from asyncio import TimeoutError
  11. class ValidityTester(object):
  12. """
  13. 检验器,负责对未知的代理进行异步检测。
  14. """
  15. # 用百度的首页来检验
  16. test_api = TEST_API
  17. def __init__(self):
  18. self._raw_proxies = None
  19. self._usable_proxies = []
  20. def set_raw_proxies(self, proxies):
  21. """
  22. 设置待检测的代理。
  23. """
  24. self._raw_proxies = proxies
  25. self._usable_proxies = []
  26. async def test_single_proxy(self, proxy):
  27. """
  28. 检测单个代理,如果可用,则将其加入_usable_proxies
  29. """
  30. async with aiohttp.ClientSession() as session:
  31. try:
  32. if isinstance(proxy, bytes):
  33. proxy = proxy.decode('utf-8')
  34. real_proxy = 'http://' + proxy
  35. print('Testing', proxy)
  36. async with session.get(self.test_api, proxy=real_proxy, timeout=15) as response:
  37. if response.status == 200:
  38. self._usable_proxies.append(proxy)
  39. print('Valid proxy', proxy)
  40. except (ProxyConnectionError, TimeoutError):
  41. print('Invalid proxy', proxy)
  42. def test(self):
  43. """
  44. 异步检测_raw_proxies中的全部代理。
  45. """
  46. print('ValidityTester is working')
  47. try:
  48. loop = asyncio.get_event_loop()
  49. tasks = [self.test_single_proxy(proxy) for proxy in self._raw_proxies]
  50. loop.run_until_complete(asyncio.wait(tasks))
  51. except ValueError:
  52. print('Async Error')
  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. while 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. proxies = self._tester.get_usable_proxies()
  86. self._conn.put_many(proxies)
  87. proxy_count += len(raw_proxies)
  88. if self.is_over_threshold():
  89. print('IP is enough, waiting to be used')
  90. break
  91. if proxy_count == 0:
  92. raise ResourceDepletionError
  93. class Schedule(object):
  94. """
  95. 总调度器,用于协调各调度器模块
  96. """
  97. @staticmethod
  98. def valid_proxy(cycle=VALID_CHECK_CYCLE):
  99. """
  100. 对已经如池的代理进行检测,防止池中的代理因长期
  101. 不使用而过期。
  102. 抽出代理池队列中前1/2的代理,检测,合格者压入队列尾。
  103. """
  104. conn = RedisClient()
  105. tester = ValidityTester()
  106. while True:
  107. print('Refreshing ip')
  108. count = int(0.5 * conn.queue_len)
  109. if count == 0:
  110. print('Waiting for adding')
  111. time.sleep(cycle)
  112. continue
  113. raw_proxies = conn.get(count)
  114. tester.set_raw_proxies(raw_proxies)
  115. tester.test()
  116. proxies = tester.get_usable_proxies()
  117. conn.put_many(proxies)
  118. time.sleep(cycle)
  119. @staticmethod
  120. def check_pool(lower_threshold=POOL_LOWER_THRESHOLD,
  121. upper_threshold=POOL_UPPER_THRESHOLD,
  122. cycle=POOL_LEN_CHECK_CYCLE):
  123. """
  124. 协调添加器,当代理池中可用代理的数量低于下阈值时,触发添加器,启动爬虫
  125. 补充代理,当代理达到上阈值时,添加器停止工作。
  126. """
  127. conn = RedisClient()
  128. adder = PoolAdder(upper_threshold)
  129. while True:
  130. if conn.queue_len < lower_threshold:
  131. adder.add_to_queue()
  132. time.sleep(cycle)
  133. def run(self):
  134. """
  135. 运行调度器,创建两个进程,对代理池进行维护。
  136. """
  137. print('Ip processing running')
  138. valid_process = Process(target=Schedule.valid_proxy)
  139. check_process = Process(target=Schedule.check_pool)
  140. valid_process.start()
  141. check_process.start()