1
0

login_yaoshibang.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import random
  2. import signal
  3. import socket
  4. import sys
  5. import time
  6. from commons.conn_mysql import MySQLPoolOn2
  7. from DrissionPage import ChromiumPage, ChromiumOptions
  8. from commons.Logger import logger
  9. from oss_upload.oss_upload import AliyunOSSUploader
  10. from commons.config import YSB_ACCOUNT
  11. from spiders.yaoshibang.yidun_slider_captcha import solve_slider_captcha
  12. import json
  13. import os
  14. import platform
  15. chrome_path = r"C:\Program Files\Google\Chrome\Application\chrome.exe"
  16. class YaoShiBangLogin:
  17. def __init__(self, product=None):
  18. self.product = product
  19. self.driver = None
  20. self.account_name = None
  21. self.platform = 5
  22. self.phone = None
  23. self.password = None
  24. self.db_online = MySQLPoolOn2()
  25. self.ossuploader = AliyunOSSUploader()
  26. self._register_signal_handler()
  27. def _register_signal_handler(self):
  28. def handler(signum, frame):
  29. logger.info("收到退出信号,正在关闭浏览器...")
  30. self._quit_browser()
  31. sys.exit(0)
  32. signal.signal(signal.SIGINT, handler)
  33. if hasattr(signal, "SIGTERM"):
  34. signal.signal(signal.SIGTERM, handler)
  35. def _quit_browser(self):
  36. if self.driver:
  37. try:
  38. self.driver.quit()
  39. except Exception:
  40. pass
  41. self.driver = None
  42. @staticmethod
  43. def _get_free_port():
  44. """获取一个当前可用的本地端口,供 Chrome 调试使用。"""
  45. with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
  46. s.bind(("127.0.0.1", 0))
  47. return s.getsockname()[1]
  48. import platform
  49. import os
  50. def init_browser(self):
  51. # ---------- 1. 根据操作系统选择 Chrome 路径 ----------
  52. system = platform.system()
  53. if system == "Windows":
  54. chrome_path = r"C:\Program Files\Google\Chrome\Application\chrome.exe"
  55. elif system == "Linux":
  56. chrome_path = "/usr/bin/google-chrome" # 若用 Chromium 则改为 "/usr/bin/chromium-browser"
  57. else: # macOS 或其他
  58. chrome_path = None # 让 DrissionPage 自动尝试查找
  59. co = ChromiumOptions()
  60. if chrome_path:
  61. co.set_browser_path(chrome_path)
  62. # ---------- 2. 用户数据目录(跨平台通用) ----------
  63. user_dir = os.path.abspath(f"./spiders/yaoshibang/{self.account_name}")
  64. co.set_user_data_path(user_dir)
  65. # ---------- 3. 调试端口(可选,Windows/Linux 均支持) ----------
  66. debug_port = self._get_free_port()
  67. co.set_local_port(debug_port)
  68. co.set_argument(f"--remote-debugging-port={debug_port}")
  69. co.set_argument("--remote-debugging-address=127.0.0.1")
  70. # ---------- 4. 无头模式 + 稳定性参数(全部通过 set_argument 设置,跨平台安全) ----------
  71. co.set_argument("--headless") # 可用 "--headless=new" 替代新版无头
  72. co.set_argument("--no-sandbox") # Linux 必需,Windows 下被 Chrome 忽略
  73. co.set_argument("--disable-dev-shm-usage") # Linux 防 /dev/shm 爆满
  74. co.set_argument("--disable-gpu") # 无头模式下可防 GPU 问题
  75. co.set_argument("--disable-setuid-sandbox")
  76. co.set_argument("--disable-software-rasterizer")
  77. co.set_argument("--disable-extensions")
  78. co.set_argument("--disable-plugins")
  79. co.set_argument("--no-first-run")
  80. co.set_argument("--no-default-browser-check")
  81. co.set_argument("--lang=zh-CN")
  82. self.driver = ChromiumPage(co)
  83. def _is_logged_in(self):
  84. # 与当前账号店铺展示文案一致;换店后需同步修改或改为配置项
  85. title = self.driver.ele(
  86. "xpath=//span[@class='logout']",
  87. timeout=8,
  88. )
  89. return bool(title)
  90. def login(self, account_dict):
  91. logger.info("开始登录药师帮")
  92. self.driver.get("https://dian.ysbang.cn/#/login", timeout=15)
  93. self.driver.wait.doc_loaded(timeout=10)
  94. time.sleep(2)
  95. input_name = self.driver.ele("xpath://input[@name='userAccount']", timeout=5)
  96. if not input_name:
  97. logger.error("未找到账号输入框")
  98. return False
  99. input_name.input(account_dict["phone"])
  100. time.sleep(random.uniform(1.5, 2.5))
  101. input_pass = self.driver.ele("xpath://input[@name='password']", timeout=5)
  102. if not input_pass:
  103. logger.error("未找到密码输入框")
  104. return False
  105. input_pass.input(account_dict["password"])
  106. time.sleep(random.uniform(1.5, 2.5))
  107. login_btn = self.driver.ele("xpath://button[text()='登录']", timeout=5)
  108. if not login_btn:
  109. logger.error("未找到登录按钮")
  110. return False
  111. login_btn.click()
  112. time.sleep(3)
  113. for i in range(3):
  114. solve_slider_captcha(self.driver)
  115. time.sleep(3)
  116. if self._is_logged_in():
  117. time.sleep(3)
  118. logger.info("登录成功")
  119. cookies_list = self.driver.cookies()
  120. self.save_cookies(cookies_list)
  121. return True
  122. logger.error("登录后未检测到目标店铺名,登录可能失败")
  123. return False
  124. def save_cookies(self, cookies_list):
  125. cookies_dict = {c['name']: c['value'] for c in cookies_list}
  126. timestamp = int(time.time())
  127. next_update_time = timestamp + random.randint(3600, 7200)
  128. # 保存 cookie 到文件
  129. update_sql = f""" UPDATE `accounts_platform` SET `cookie_timestamp` = %s, `cookie_str`= %s,`cookie_update_time` = %s, `status`= %s WHERE `name` = %s; """
  130. self.db_online.execute(update_sql,
  131. (timestamp, json.dumps(cookies_dict), next_update_time, 1, self.account_name))
  132. logger.info("cookie已保存成功")
  133. def search(self, account_dict):
  134. self.driver.get("https://dian.ysbang.cn/#/home", timeout=15)
  135. self.driver.wait.doc_loaded(timeout=10)
  136. time.sleep(2)
  137. if not self._is_logged_in():
  138. if not self.login(account_dict):
  139. return False
  140. else:
  141. cookies_list = self.driver.cookies()
  142. self.save_cookies(cookies_list)
  143. def get_account(self):
  144. sql_account = f""" select `id`,`name`,`ip`,`phone`,`password`,`cookie_timestamp`,`cookie_str` from `accounts_platform` where `platform`=5 and `status`=1 and `equipment_id`=3 order by `cookie_timestamp` asc limit 1 """
  145. account_list = self.db_online.select_data(sql_account)
  146. if not account_list:
  147. return {}
  148. account_dict = account_list[0]
  149. self.account_name = account_dict["name"]
  150. self.phone = account_dict["phone"]
  151. self.password = account_dict["password"]
  152. return account_dict
  153. def run(self):
  154. account_dict = self.get_account()
  155. if not account_dict:
  156. logger.error("ysb无账号可用")
  157. print(account_dict)
  158. try:
  159. self.init_browser()
  160. self.search(account_dict)
  161. except Exception as e:
  162. logger.exception("运行异常: %s", e)
  163. finally:
  164. self._quit_browser()
  165. if __name__ == "__main__":
  166. YaoShiBangLogin().run()