schedule.py 4.4 KB

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