run_all.py 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. # -*- coding: utf-8 -*-
  2. # Author : Charley
  3. # Python : 3.12.10
  4. # Date : 2026/08/21
  5. """集物星球爬虫一键启动:把 5 个常驻任务各自拉成独立子进程并守护(崩溃自动重启)。
  6. 服务器部署只跑这一个文件即可:`python run_all.py`。
  7. 每个任务是独立子进程(各自维护自己的 schedule 定时与日志、互不干扰),本脚本轮询它们的存活状态,
  8. 发现哪个退出就隔 RESTART_DELAY 秒重启那个(无人值守长跑用)。
  9. 想单独调试某个任务时,仍可直接 `python jw_xxx.py` 单跑,不必用本文件。
  10. """
  11. import os
  12. import sys
  13. import time
  14. import subprocess
  15. from loguru import logger
  16. # 待启动的常驻任务:(展示名, 脚本文件)
  17. TASKS = [
  18. ("在售抓取", "jw_onsale_spider.py"), # 每天 09/15/20/01 四档
  19. ("已售抓取", "jw_sold_spider.py"), # 每天 08:00(含拆卡报告/购买记录)
  20. ("在售日报", "jw_onsale_report.py"), # 每天 09/15/20/01 四档发企微
  21. ("已售日报", "jw_sold_report.py"), # 每天 09:10 发企微
  22. ("在售监控", "jw_onsale_alert.py"), # 常驻轮询、上架/过半/结束提醒
  23. ]
  24. RESTART_DELAY = 5 # 子进程退出后重启前的等待秒数
  25. CHECK_INTERVAL = 10 # 存活巡检间隔秒数
  26. BASE_DIR = os.path.dirname(os.path.abspath(__file__))
  27. logger.remove()
  28. # logger.add(sys.stderr, level="INFO", format="[{time:YYYY-MM-DD HH:mm:ss}] {level} {message}")
  29. logger.add("./logs/run_all_{time:YYYYMMDD}.log", encoding="utf-8", rotation="00:00",
  30. format="[{time:YYYY-MM-DD HH:mm:ss.SSS}] {level} {message}", level="INFO", retention="7 day")
  31. def start_one(script: str) -> subprocess.Popen:
  32. """用当前 Python 解释器在项目目录下拉起一个任务子进程。
  33. Args:
  34. script (str): 任务脚本文件名(相对项目目录)。
  35. Returns:
  36. subprocess.Popen: 已启动的子进程句柄。
  37. """
  38. return subprocess.Popen([sys.executable, os.path.join(BASE_DIR, script)], cwd=BASE_DIR)
  39. def main() -> None:
  40. """启动全部任务并守护:巡检存活,退出的子进程自动重启(无限守护)。"""
  41. procs = {name: [script, start_one(script)] for name, script in TASKS}
  42. logger.success(f"已启动 {len(procs)} 个任务:{', '.join(procs.keys())}")
  43. try:
  44. while True:
  45. for name, item in procs.items():
  46. script, proc = item
  47. if proc.poll() is not None: # 该子进程已退出 → 重启
  48. logger.warning(f"[{name}] {script} 退出(code={proc.returncode}),{RESTART_DELAY}s 后重启")
  49. time.sleep(RESTART_DELAY)
  50. item[1] = start_one(script)
  51. logger.info(f"[{name}] {script} 已重启")
  52. time.sleep(CHECK_INTERVAL)
  53. except KeyboardInterrupt: # Ctrl+C:优雅关掉所有子进程
  54. logger.info("收到退出信号,正在关闭所有子进程" + "." * 10)
  55. for name, (script, proc) in procs.items():
  56. proc.terminate()
  57. logger.info("已全部关闭")
  58. if __name__ == "__main__":
  59. main()