cookie_pool.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import time
  2. from collections import Iterable
  3. from func_timeout import func_set_timeout
  4. import feapder.utils.tools as tools
  5. from feapder.db.mongodb import MongoDB
  6. from feapder.db.redisdb import RedisDB
  7. from feapder.network.cookie_pool import (
  8. CookiePoolInterface,
  9. PageCookiePool,
  10. User,
  11. )
  12. from feapder.utils.log import log
  13. from feapder.utils.redis_lock import RedisLock
  14. from feapder.utils.tools import get_current_date
  15. __all__ = [
  16. "PageCookiePool",
  17. "User",
  18. "LoginCookiePool"
  19. ]
  20. class LoginCookiePool(CookiePoolInterface):
  21. """
  22. 需要登陆的cookie池, 用户账号密码等信息用mongoDB保存
  23. """
  24. def __init__(
  25. self,
  26. redis_key,
  27. *,
  28. login_site,
  29. table_userbase="feapder_login",
  30. table_login_record="feapder_login_record",
  31. login_state_key="login_state",
  32. lock_state_key="lock_state",
  33. username_key="username",
  34. password_key="password",
  35. login_retry_times=10,
  36. ):
  37. """
  38. @param redis_key: 项目名
  39. @param login_site: 网站名称
  40. @param table_userbase: 用户表名
  41. @param table_login_record: 用户登录状态表名
  42. @param login_state_key: 登录状态列名
  43. @param lock_state_key: 封锁状态列名
  44. @param username_key: 登陆名列名
  45. @param password_key: 密码列名
  46. @param login_retry_times: 登陆失败重试次数
  47. """
  48. self._tab_cookie_pool = "{}:l_cookie_pool".format(redis_key)
  49. self._login_retry_times = login_retry_times
  50. self._table_userbase = table_userbase
  51. self._table_login_record = table_login_record
  52. self._login_state_key = login_state_key
  53. self._lock_state_key = lock_state_key
  54. self._username_key = username_key
  55. self._password_key = password_key
  56. self._login_site = login_site
  57. self._redisdb = RedisDB()
  58. self._mongo = MongoDB(db='user_login')
  59. def create_cookie(self, username, password):
  60. """
  61. 创建cookie
  62. @param username: 用户名
  63. @param password: 密码
  64. @return: return cookie / None
  65. """
  66. raise NotImplementedError
  67. def get_user_info(self):
  68. """
  69. 返回用户信息
  70. @return: yield username, password
  71. """
  72. query = {
  73. "site": self._login_site,
  74. self._lock_state_key: 0,
  75. self._login_state_key: 0
  76. }
  77. return self._mongo.find(self._table_userbase, query)
  78. def handle_login_failed_user(self, username, password):
  79. """
  80. 处理登录失败的user
  81. @param username:
  82. @param password:
  83. @return:
  84. """
  85. pass
  86. def handel_exception(self, e):
  87. """
  88. 处理异常
  89. @param e:
  90. @return:
  91. """
  92. log.exception(e)
  93. def save_cookie(self, username, cookie):
  94. user_cookie = {"username": username, "cookie": cookie}
  95. self._redisdb.lpush(self._tab_cookie_pool, user_cookie)
  96. date_str = time.strftime(
  97. "%Y-%m-%d %H:%M:%S",
  98. time.localtime(int(round(time.time())))
  99. )
  100. self._mongo.add(
  101. coll_name=self._table_login_record,
  102. data={
  103. self._login_state_key: 1,
  104. "status": "create",
  105. "site": self._login_site,
  106. "login_time": date_str
  107. },
  108. update_columns=self._username_key,
  109. update_columns_value=username)
  110. @func_set_timeout(60)
  111. def get_cookie(self, wait_when_null=True, sleeptime=25) -> User:
  112. while True:
  113. try:
  114. user_cookie = self._redisdb.rpoplpush(self._tab_cookie_pool)
  115. if not user_cookie and wait_when_null:
  116. tools.delay_time(sleeptime)
  117. log.info("暂无cookie 生产中...")
  118. self.login()
  119. continue
  120. if user_cookie:
  121. user_cookie = eval(user_cookie)
  122. return User(**user_cookie)
  123. return None
  124. except Exception as e:
  125. log.exception(e)
  126. tools.delay_time(1)
  127. def del_cookie(self, user: User):
  128. """
  129. 删除失效的cookie
  130. @param user:
  131. @return:
  132. """
  133. user_info = {"username": user.username, "cookie": user.cookie}
  134. self._redisdb.lrem(self._tab_cookie_pool, user_info)
  135. self._mongo.add(
  136. coll_name=self._table_login_record,
  137. data={
  138. self._login_state_key: 1,
  139. "status": "remove",
  140. "site": self._login_site,
  141. "login_time": get_current_date()
  142. },
  143. update_columns=self._username_key,
  144. update_columns_value=user.username)
  145. def user_is_locked(self, user: User):
  146. self._mongo.add(
  147. coll_name=self._table_login_record,
  148. data={
  149. self._lock_state_key: 1,
  150. "site": self._login_site,
  151. "login_time": get_current_date()
  152. },
  153. update_columns=self._username_key,
  154. update_columns_value=user.username)
  155. def run(self):
  156. with RedisLock(key=self._tab_cookie_pool, lock_timeout=3600, wait_timeout=100) as _lock:
  157. if _lock.locked:
  158. user_infos = self.get_user_info()
  159. if not isinstance(user_infos, Iterable):
  160. raise ValueError("get_user_info 返回值必须可迭代")
  161. if not user_infos:
  162. log.info("无可用用户")
  163. for info in user_infos:
  164. username = info.get("username")
  165. password = info.get("password")
  166. for i in range(self._login_retry_times):
  167. try:
  168. cookie = self.create_cookie(username, password)
  169. if cookie:
  170. self.save_cookie(username, cookie)
  171. else:
  172. self.handle_login_failed_user(username, password)
  173. break
  174. except Exception as e:
  175. self.handel_exception(e)
  176. else:
  177. self.handle_login_failed_user(username, password)
  178. login = run