taobao_login.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import time
  2. import json
  3. import random
  4. import signal
  5. import sys
  6. from DrissionPage import ChromiumPage, ChromiumOptions
  7. import socket
  8. from commons.conn_mysql import MySQLPoolOn2
  9. from commons.Logger import logger
  10. from commons.config import CHROME_PATH
  11. MAX_PAGES = 5
  12. WAIT_BETWEEN_PAGES = (8, 15) # 页间等待时间范围(秒)
  13. SCROLL_DELAY = (0.3, 0.8) # 滚动延迟范围
  14. CLICK_DELAY = (0.5, 1.2) # 点击延迟范围
  15. BROWSE_TIME = (5, 10) # 浏览时间范围
  16. chrome_path = CHROME_PATH
  17. class TaobaoAutoCrawl:
  18. def __init__(self, account_name, ip, key_word):
  19. self.driver = None
  20. self.register_signal_handler()
  21. self.db = MySQLPoolOn2()
  22. self.account_name = account_name
  23. self.ip = ip
  24. self.keyword = key_word
  25. @staticmethod
  26. def _get_free_port():
  27. """获取一个当前可用的本地端口,供 Chrome 调试使用。"""
  28. with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
  29. s.bind(("127.0.0.1", 0))
  30. return s.getsockname()[1]
  31. def init_drissionpage(self):
  32. # 避免 auto_port 在部分环境下生成异常地址(无端口)导致 ChromiumPage 初始化失败
  33. co = ChromiumOptions().set_browser_path(chrome_path)
  34. debug_port = self._get_free_port()
  35. co.set_argument(f"--remote-debugging-port={debug_port}")
  36. co.set_user_data_path(f"./account_cache/taobao/{self.account_name}")
  37. if self.ip:
  38. proxy = self.ip.strip()
  39. if not proxy.startswith(("http://", "https://")):
  40. proxy = f"http://{proxy}"
  41. co.set_argument(f"--proxy-server={proxy}")
  42. logger.info(f"启动浏览器: account={self.account_name}, debug_port={debug_port}")
  43. self.driver = ChromiumPage(co)
  44. def register_signal_handler(self):
  45. """ 非常必要,注册信号处理,确保状态保存"""
  46. def signal_handler(signum, frame):
  47. print("\n⚠️ 收到退出信号,正在保存状态并退出...")
  48. if self.driver:
  49. self.driver.quit()
  50. sys.exit(0)
  51. signal.signal(signal.SIGINT, signal_handler)
  52. # Windows 上可能不支持 SIGTERM,做兼容处理
  53. if hasattr(signal, "SIGTERM"):
  54. signal.signal(signal.SIGTERM, signal_handler)
  55. # ==================== 人工行为模拟 ====================
  56. def random_wait(self, min_sec, max_sec=None):
  57. """随机等待"""
  58. if max_sec is None:
  59. max_sec = min_sec
  60. time.sleep(random.uniform(min_sec, max_sec))
  61. def move_mouse_to_element(self, element):
  62. """移动鼠标到元素"""
  63. if not element:
  64. return
  65. try:
  66. # 优先使用 DrissionPage 推荐方式
  67. self.driver.actions.move_to(element)
  68. except Exception:
  69. # 兼容旧逻辑:按元素中心点移动(不同版本 move 参数可能不同)
  70. box = element.rect
  71. try:
  72. center_x = int(box.x + box.width / 2)
  73. center_y = int(box.y + box.height / 2)
  74. except Exception:
  75. center_x = int(box["x"] + box["width"] / 2)
  76. center_y = int(box["y"] + box["height"] / 2)
  77. try:
  78. self.driver.actions.move(center_x, center_y)
  79. except TypeError:
  80. # 某些版本仅支持关键字参数
  81. self.driver.actions.move(offset_x=center_x, offset_y=center_y)
  82. self.random_wait(0.2, 0.5)
  83. def human_type(self, element, text):
  84. """模拟人类输入"""
  85. for char in text:
  86. element.send_keys(char)
  87. time.sleep(random.uniform(0.1, 0.3))
  88. def login(self, username, password):
  89. self.driver.get("https://login.taobao.com")
  90. self.random_wait(5, 8)
  91. # 输入账号
  92. login_name = self.driver.ele("xpath=//input[@name='fm-login-id']", timeout=30)
  93. if login_name:
  94. self.move_mouse_to_element(login_name)
  95. self.human_type(login_name, username)
  96. self.random_wait(1, 3)
  97. # 输入密码
  98. login_pass = self.driver.ele("xpath=//input[@name='fm-login-password']", timeout=30)
  99. if login_pass:
  100. self.move_mouse_to_element(login_pass)
  101. self.human_type(login_pass, password)
  102. self.random_wait(1, 3)
  103. # 点击登录
  104. login_button = self.driver.ele("xpath=//button[text()='登录']", timeout=30)
  105. if login_button:
  106. self.move_mouse_to_element(login_button)
  107. login_button.click()
  108. self.random_wait(1, 3)
  109. # 处理同意按钮
  110. login_agree = self.driver.ele("xpath=//button[text()='同意']", timeout=5)
  111. if login_agree:
  112. self.move_mouse_to_element(login_agree)
  113. login_agree.click()
  114. self.random_wait(1, 3)
  115. # 等待登录结果
  116. self.random_wait(10, 20)
  117. # 检查是否登录成功
  118. user_info = self.driver.ele("xpath=//a[@class='site-nav-login-info-nick']", timeout=10)
  119. if user_info:
  120. print("登录成功!")
  121. else:
  122. print("登录失败,请检查账号密码或验证码")
  123. def get_search(self):
  124. url = "https://www.taobao.com"
  125. self.driver.get(url, timeout=30)
  126. time.sleep(30)
  127. time.sleep(random.uniform(3, 8))
  128. # 刷新一次,否则可能未找到登录状态
  129. self.driver.refresh()
  130. self.random_wait(5, 10)
  131. # login_name = self.driver.ele("xpath=//input[@name='fm-login-id']")
  132. # if login_name:
  133. # self.login("aqwwer","wewetrv")
  134. # self.driver.refresh()
  135. # self.random_wait(5, 10)
  136. ele_iframe = self.driver.ele("xpath=//iframe[@id='baxia-dialog-content']")
  137. if ele_iframe:
  138. update_sql = f""" UPDATE `retrieve_collect_equipment_account` SET `status`= %s WHERE `nickname` = %s; """
  139. self.db.execute(update_sql, (1, self.account_name))
  140. return False
  141. ele = self.driver.ele('xpath=//*[contains(@class,"site-nav-login-info-nick")]', timeout=30)
  142. if ele:
  143. cookies_list = self.driver.cookies()
  144. cookies_dict = {c['name']: c['value'] for c in cookies_list}
  145. timestamp = int(time.time())
  146. # 保存 cookie 到文件
  147. update_sql = f""" UPDATE `retrieve_collect_equipment_account` SET `update_time` = %s, `cookie_str`= %s,`status`= %s WHERE `username` = %s; """
  148. self.db.execute(update_sql, (timestamp, json.dumps(cookies_dict), 0, self.account_name))
  149. print(f"{self.account_name},获取 cookie 成功!")
  150. logger.info(f"{self.account_name},获取 cookie 成功!")
  151. self.random_wait(3, 5)
  152. return True
  153. else:
  154. return False
  155. def run(self):
  156. bool_login = False
  157. try:
  158. self.init_drissionpage()
  159. bool_login = self.get_search()
  160. except Exception as e:
  161. logger.exception(f"{self.account_name} 获取 cookie 异常: {e}")
  162. finally:
  163. if self.driver:
  164. self.driver.quit()
  165. return bool(bool_login)