cookie_pool.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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. self._mongo.add(
  97. coll_name=self._table_login_record,
  98. data={self._login_state_key: 1,
  99. "status": "create",
  100. "site": self._login_site,
  101. "login_time": time.strftime("%Y-%m-%d %H:%M:%S",
  102. time.localtime(
  103. int(round(time.time()))))},
  104. update_columns=self._username_key,
  105. update_columns_value=username)
  106. @func_set_timeout(60)
  107. def get_cookie(self, wait_when_null=True) -> User:
  108. while True:
  109. try:
  110. user_cookie = self._redisdb.rpoplpush(self._tab_cookie_pool)
  111. if not user_cookie and wait_when_null:
  112. log.info("暂无cookie 生产中...")
  113. self.login()
  114. continue
  115. if user_cookie:
  116. user_cookie = eval(user_cookie)
  117. return User(**user_cookie)
  118. return None
  119. except Exception as e:
  120. log.exception(e)
  121. tools.delay_time(1)
  122. def del_cookie(self, user: User):
  123. """
  124. 删除失效的cookie
  125. @param user:
  126. @return:
  127. """
  128. user_info = {"username": user.username, "cookie": user.cookie}
  129. self._redisdb.lrem(self._tab_cookie_pool, user_info)
  130. self._mongo.add(
  131. coll_name=self._table_login_record,
  132. data={
  133. self._login_state_key: 1,
  134. "status": "remove",
  135. "site": self._login_site,
  136. "login_time": get_current_date()
  137. },
  138. update_columns=self._username_key,
  139. update_columns_value=user.username)
  140. def user_is_locked(self, user: User):
  141. self._mongo.add(
  142. coll_name=self._table_login_record,
  143. data={
  144. self._lock_state_key: 1,
  145. "site": self._login_site,
  146. "login_time": get_current_date()
  147. },
  148. update_columns=self._username_key,
  149. update_columns_value=user.username)
  150. def run(self):
  151. with RedisLock(
  152. key=self._tab_cookie_pool, lock_timeout=3600, wait_timeout=100
  153. ) as _lock:
  154. if _lock.locked:
  155. user_infos = self.get_user_info()
  156. if not isinstance(user_infos, Iterable):
  157. raise ValueError("get_user_info 返回值必须可迭代")
  158. if not user_infos:
  159. log.info("无可用用户")
  160. for info in user_infos:
  161. username = info.get("username")
  162. password = info.get("password")
  163. for i in range(self._login_retry_times):
  164. try:
  165. cookie = self.create_cookie(username, password)
  166. if cookie:
  167. self.save_cookie(username, cookie)
  168. else:
  169. self.handle_login_failed_user(username,
  170. password)
  171. break
  172. except Exception as e:
  173. self.handel_exception(e)
  174. else:
  175. self.handle_login_failed_user(username, password)
  176. login = run