|
@@ -0,0 +1,472 @@
|
|
|
|
|
+#!/usr/bin/env python3
|
|
|
|
|
+# -*- coding: utf-8 -*-
|
|
|
|
|
+"""
|
|
|
|
|
+新闻爬虫容器管理工具 - supercronic
|
|
|
|
|
+"""
|
|
|
|
|
+
|
|
|
|
|
+import os
|
|
|
|
|
+import sys
|
|
|
|
|
+import subprocess
|
|
|
|
|
+import time
|
|
|
|
|
+from pathlib import Path
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def run_command(cmd, shell=True, capture_output=True):
|
|
|
|
|
+ """执行系统命令"""
|
|
|
|
|
+ try:
|
|
|
|
|
+ result = subprocess.run(
|
|
|
|
|
+ cmd, shell=shell, capture_output=capture_output, text=True
|
|
|
|
|
+ )
|
|
|
|
|
+ return result.returncode == 0, result.stdout, result.stderr
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ return False, "", str(e)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def manual_run():
|
|
|
|
|
+ """手动执行一次爬虫"""
|
|
|
|
|
+ print("🔄 手动执行爬虫...")
|
|
|
|
|
+ try:
|
|
|
|
|
+ result = subprocess.run(
|
|
|
|
|
+ ["python", "main.py"], cwd="/app", capture_output=False, text=True
|
|
|
|
|
+ )
|
|
|
|
|
+ if result.returncode == 0:
|
|
|
|
|
+ print("✅ 执行完成")
|
|
|
|
|
+ else:
|
|
|
|
|
+ print(f"❌ 执行失败,退出码: {result.returncode}")
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f"❌ 执行出错: {e}")
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def parse_cron_schedule(cron_expr):
|
|
|
|
|
+ """解析cron表达式并返回人类可读的描述"""
|
|
|
|
|
+ if not cron_expr or cron_expr == "未设置":
|
|
|
|
|
+ return "未设置"
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ parts = cron_expr.strip().split()
|
|
|
|
|
+ if len(parts) != 5:
|
|
|
|
|
+ return f"原始表达式: {cron_expr}"
|
|
|
|
|
+
|
|
|
|
|
+ minute, hour, day, month, weekday = parts
|
|
|
|
|
+
|
|
|
|
|
+ # 分析分钟
|
|
|
|
|
+ if minute == "*":
|
|
|
|
|
+ minute_desc = "每分钟"
|
|
|
|
|
+ elif minute.startswith("*/"):
|
|
|
|
|
+ interval = minute[2:]
|
|
|
|
|
+ minute_desc = f"每{interval}分钟"
|
|
|
|
|
+ elif "," in minute:
|
|
|
|
|
+ minute_desc = f"在第{minute}分钟"
|
|
|
|
|
+ else:
|
|
|
|
|
+ minute_desc = f"在第{minute}分钟"
|
|
|
|
|
+
|
|
|
|
|
+ # 分析小时
|
|
|
|
|
+ if hour == "*":
|
|
|
|
|
+ hour_desc = "每小时"
|
|
|
|
|
+ elif hour.startswith("*/"):
|
|
|
|
|
+ interval = hour[2:]
|
|
|
|
|
+ hour_desc = f"每{interval}小时"
|
|
|
|
|
+ elif "," in hour:
|
|
|
|
|
+ hour_desc = f"在{hour}点"
|
|
|
|
|
+ else:
|
|
|
|
|
+ hour_desc = f"在{hour}点"
|
|
|
|
|
+
|
|
|
|
|
+ # 分析日期
|
|
|
|
|
+ if day == "*":
|
|
|
|
|
+ day_desc = "每天"
|
|
|
|
|
+ elif day.startswith("*/"):
|
|
|
|
|
+ interval = day[2:]
|
|
|
|
|
+ day_desc = f"每{interval}天"
|
|
|
|
|
+ else:
|
|
|
|
|
+ day_desc = f"每月{day}号"
|
|
|
|
|
+
|
|
|
|
|
+ # 分析月份
|
|
|
|
|
+ if month == "*":
|
|
|
|
|
+ month_desc = "每月"
|
|
|
|
|
+ else:
|
|
|
|
|
+ month_desc = f"在{month}月"
|
|
|
|
|
+
|
|
|
|
|
+ # 分析星期
|
|
|
|
|
+ weekday_names = {
|
|
|
|
|
+ "0": "周日", "1": "周一", "2": "周二", "3": "周三",
|
|
|
|
|
+ "4": "周四", "5": "周五", "6": "周六", "7": "周日"
|
|
|
|
|
+ }
|
|
|
|
|
+ if weekday == "*":
|
|
|
|
|
+ weekday_desc = ""
|
|
|
|
|
+ else:
|
|
|
|
|
+ weekday_desc = f"在{weekday_names.get(weekday, weekday)}"
|
|
|
|
|
+
|
|
|
|
|
+ # 组合描述
|
|
|
|
|
+ if minute.startswith("*/") and hour == "*" and day == "*" and month == "*" and weekday == "*":
|
|
|
|
|
+ # 简单的间隔模式,如 */30 * * * *
|
|
|
|
|
+ return f"每{minute[2:]}分钟执行一次"
|
|
|
|
|
+ elif hour != "*" and minute != "*" and day == "*" and month == "*" and weekday == "*":
|
|
|
|
|
+ # 每天特定时间,如 0 9 * * *
|
|
|
|
|
+ return f"每天{hour}:{minute.zfill(2)}执行"
|
|
|
|
|
+ elif weekday != "*" and day == "*":
|
|
|
|
|
+ # 每周特定时间
|
|
|
|
|
+ return f"{weekday_desc}{hour}:{minute.zfill(2)}执行"
|
|
|
|
|
+ else:
|
|
|
|
|
+ # 复杂模式,显示详细信息
|
|
|
|
|
+ desc_parts = [part for part in [month_desc, day_desc, weekday_desc, hour_desc, minute_desc] if part and part != "每月" and part != "每天" and part != "每小时"]
|
|
|
|
|
+ if desc_parts:
|
|
|
|
|
+ return " ".join(desc_parts) + "执行"
|
|
|
|
|
+ else:
|
|
|
|
|
+ return f"复杂表达式: {cron_expr}"
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ return f"解析失败: {cron_expr}"
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def show_status():
|
|
|
|
|
+ """显示容器状态"""
|
|
|
|
|
+ print("📊 容器状态:")
|
|
|
|
|
+
|
|
|
|
|
+ # 检查 PID 1 状态
|
|
|
|
|
+ supercronic_is_pid1 = False
|
|
|
|
|
+ pid1_cmdline = ""
|
|
|
|
|
+ try:
|
|
|
|
|
+ with open('/proc/1/cmdline', 'r') as f:
|
|
|
|
|
+ pid1_cmdline = f.read().replace('\x00', ' ').strip()
|
|
|
|
|
+ print(f" 🔍 PID 1 进程: {pid1_cmdline}")
|
|
|
|
|
+
|
|
|
|
|
+ if "supercronic" in pid1_cmdline.lower():
|
|
|
|
|
+ print(" ✅ supercronic 正确运行为 PID 1")
|
|
|
|
|
+ supercronic_is_pid1 = True
|
|
|
|
|
+ else:
|
|
|
|
|
+ print(" ❌ PID 1 不是 supercronic")
|
|
|
|
|
+ print(f" 📋 实际的 PID 1: {pid1_cmdline}")
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f" ❌ 无法读取 PID 1 信息: {e}")
|
|
|
|
|
+
|
|
|
|
|
+ # 检查环境变量
|
|
|
|
|
+ cron_schedule = os.environ.get("CRON_SCHEDULE", "未设置")
|
|
|
|
|
+ run_mode = os.environ.get("RUN_MODE", "未设置")
|
|
|
|
|
+ immediate_run = os.environ.get("IMMEDIATE_RUN", "未设置")
|
|
|
|
|
+
|
|
|
|
|
+ print(f" ⚙️ 运行配置:")
|
|
|
|
|
+ print(f" CRON_SCHEDULE: {cron_schedule}")
|
|
|
|
|
+
|
|
|
|
|
+ # 解析并显示cron表达式的含义
|
|
|
|
|
+ cron_description = parse_cron_schedule(cron_schedule)
|
|
|
|
|
+ print(f" ⏰ 执行频率: {cron_description}")
|
|
|
|
|
+
|
|
|
|
|
+ print(f" RUN_MODE: {run_mode}")
|
|
|
|
|
+ print(f" IMMEDIATE_RUN: {immediate_run}")
|
|
|
|
|
+
|
|
|
|
|
+ # 检查配置文件
|
|
|
|
|
+ config_files = ["/app/config/config.yaml", "/app/config/frequency_words.txt"]
|
|
|
|
|
+ print(" 📁 配置文件:")
|
|
|
|
|
+ for file_path in config_files:
|
|
|
|
|
+ if Path(file_path).exists():
|
|
|
|
|
+ print(f" ✅ {Path(file_path).name}")
|
|
|
|
|
+ else:
|
|
|
|
|
+ print(f" ❌ {Path(file_path).name} 缺失")
|
|
|
|
|
+
|
|
|
|
|
+ # 检查关键文件
|
|
|
|
|
+ key_files = [
|
|
|
|
|
+ ("/usr/local/bin/supercronic-linux-amd64", "supercronic二进制文件"),
|
|
|
|
|
+ ("/usr/local/bin/supercronic", "supercronic软链接"),
|
|
|
|
|
+ ("/tmp/crontab", "crontab文件"),
|
|
|
|
|
+ ("/entrypoint.sh", "启动脚本")
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ print(" 📂 关键文件检查:")
|
|
|
|
|
+ for file_path, description in key_files:
|
|
|
|
|
+ if Path(file_path).exists():
|
|
|
|
|
+ print(f" ✅ {description}: 存在")
|
|
|
|
|
+ # 对于crontab文件,显示内容
|
|
|
|
|
+ if file_path == "/tmp/crontab":
|
|
|
|
|
+ try:
|
|
|
|
|
+ with open(file_path, 'r') as f:
|
|
|
|
|
+ crontab_content = f.read().strip()
|
|
|
|
|
+ print(f" 内容: {crontab_content}")
|
|
|
|
|
+ except:
|
|
|
|
|
+ pass
|
|
|
|
|
+ else:
|
|
|
|
|
+ print(f" ❌ {description}: 不存在")
|
|
|
|
|
+
|
|
|
|
|
+ # 检查容器运行时间
|
|
|
|
|
+ print(" ⏱️ 容器时间信息:")
|
|
|
|
|
+ try:
|
|
|
|
|
+ # 检查 PID 1 的启动时间
|
|
|
|
|
+ with open('/proc/1/stat', 'r') as f:
|
|
|
|
|
+ stat_content = f.read().strip().split()
|
|
|
|
|
+ if len(stat_content) >= 22:
|
|
|
|
|
+ # starttime 是第22个字段(索引21)
|
|
|
|
|
+ starttime_ticks = int(stat_content[21])
|
|
|
|
|
+
|
|
|
|
|
+ # 读取系统启动时间
|
|
|
|
|
+ with open('/proc/stat', 'r') as stat_f:
|
|
|
|
|
+ for line in stat_f:
|
|
|
|
|
+ if line.startswith('btime'):
|
|
|
|
|
+ boot_time = int(line.split()[1])
|
|
|
|
|
+ break
|
|
|
|
|
+ else:
|
|
|
|
|
+ boot_time = 0
|
|
|
|
|
+
|
|
|
|
|
+ # 读取系统时钟频率
|
|
|
|
|
+ clock_ticks = os.sysconf(os.sysconf_names['SC_CLK_TCK'])
|
|
|
|
|
+
|
|
|
|
|
+ if boot_time > 0:
|
|
|
|
|
+ pid1_start_time = boot_time + (starttime_ticks / clock_ticks)
|
|
|
|
|
+ current_time = time.time()
|
|
|
|
|
+ uptime_seconds = int(current_time - pid1_start_time)
|
|
|
|
|
+ uptime_minutes = uptime_seconds // 60
|
|
|
|
|
+ uptime_hours = uptime_minutes // 60
|
|
|
|
|
+
|
|
|
|
|
+ if uptime_hours > 0:
|
|
|
|
|
+ print(f" PID 1 运行时间: {uptime_hours} 小时 {uptime_minutes % 60} 分钟")
|
|
|
|
|
+ else:
|
|
|
|
|
+ print(f" PID 1 运行时间: {uptime_minutes} 分钟 ({uptime_seconds} 秒)")
|
|
|
|
|
+ else:
|
|
|
|
|
+ print(f" PID 1 运行时间: 无法精确计算")
|
|
|
|
|
+ else:
|
|
|
|
|
+ print(" ❌ 无法解析 PID 1 统计信息")
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f" ❌ 时间检查失败: {e}")
|
|
|
|
|
+
|
|
|
|
|
+ # 状态总结和建议
|
|
|
|
|
+ print(" 📊 状态总结:")
|
|
|
|
|
+ if supercronic_is_pid1:
|
|
|
|
|
+ print(" ✅ supercronic 正确运行为 PID 1")
|
|
|
|
|
+ print(" ✅ 定时任务应该正常工作")
|
|
|
|
|
+
|
|
|
|
|
+ # 显示当前的调度信息
|
|
|
|
|
+ if cron_schedule != "未设置":
|
|
|
|
|
+ print(f" ⏰ 当前调度: {cron_description}")
|
|
|
|
|
+
|
|
|
|
|
+ # 提供一些常见的调度建议
|
|
|
|
|
+ if "分钟" in cron_description and "每30分钟" not in cron_description and "每60分钟" not in cron_description:
|
|
|
|
|
+ print(" 💡 频繁执行模式,适合实时监控")
|
|
|
|
|
+ elif "小时" in cron_description:
|
|
|
|
|
+ print(" 💡 按小时执行模式,适合定期汇总")
|
|
|
|
|
+ elif "天" in cron_description:
|
|
|
|
|
+ print(" 💡 每日执行模式,适合日报生成")
|
|
|
|
|
+
|
|
|
|
|
+ print(" 💡 如果定时任务不执行,检查:")
|
|
|
|
|
+ print(" • crontab 格式是否正确")
|
|
|
|
|
+ print(" • 时区设置是否正确")
|
|
|
|
|
+ print(" • 应用程序是否有错误")
|
|
|
|
|
+ else:
|
|
|
|
|
+ print(" ❌ supercronic 状态异常")
|
|
|
|
|
+ if pid1_cmdline:
|
|
|
|
|
+ print(f" 📋 当前 PID 1: {pid1_cmdline}")
|
|
|
|
|
+ print(" 💡 建议操作:")
|
|
|
|
|
+ print(" • 重启容器: docker restart trend-radar")
|
|
|
|
|
+ print(" • 检查容器日志: docker logs trend-radar")
|
|
|
|
|
+
|
|
|
|
|
+ # 显示日志检查建议
|
|
|
|
|
+ print(" 📋 运行状态检查:")
|
|
|
|
|
+ print(" • 查看完整容器日志: docker logs trend-radar")
|
|
|
|
|
+ print(" • 查看实时日志: docker logs -f trend-radar")
|
|
|
|
|
+ print(" • 手动执行测试: python manage.py run")
|
|
|
|
|
+ print(" • 重启容器服务: docker restart trend-radar")
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def show_config():
|
|
|
|
|
+ """显示当前配置"""
|
|
|
|
|
+ print("⚙️ 当前配置:")
|
|
|
|
|
+
|
|
|
|
|
+ env_vars = [
|
|
|
|
|
+ "CRON_SCHEDULE",
|
|
|
|
|
+ "RUN_MODE",
|
|
|
|
|
+ "IMMEDIATE_RUN",
|
|
|
|
|
+ "FEISHU_WEBHOOK_URL",
|
|
|
|
|
+ "DINGTALK_WEBHOOK_URL",
|
|
|
|
|
+ "WEWORK_WEBHOOK_URL",
|
|
|
|
|
+ "TELEGRAM_BOT_TOKEN",
|
|
|
|
|
+ "TELEGRAM_CHAT_ID",
|
|
|
|
|
+ "CONFIG_PATH",
|
|
|
|
|
+ "FREQUENCY_WORDS_PATH",
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ for var in env_vars:
|
|
|
|
|
+ value = os.environ.get(var, "未设置")
|
|
|
|
|
+ # 隐藏敏感信息
|
|
|
|
|
+ if any(sensitive in var for sensitive in ["WEBHOOK", "TOKEN", "KEY"]):
|
|
|
|
|
+ if value and value != "未设置":
|
|
|
|
|
+ masked_value = value[:10] + "***" if len(value) > 10 else "***"
|
|
|
|
|
+ print(f" {var}: {masked_value}")
|
|
|
|
|
+ else:
|
|
|
|
|
+ print(f" {var}: {value}")
|
|
|
|
|
+ else:
|
|
|
|
|
+ print(f" {var}: {value}")
|
|
|
|
|
+
|
|
|
|
|
+ crontab_file = "/tmp/crontab"
|
|
|
|
|
+ if Path(crontab_file).exists():
|
|
|
|
|
+ print(" 📅 Crontab内容:")
|
|
|
|
|
+ try:
|
|
|
|
|
+ with open(crontab_file, "r") as f:
|
|
|
|
|
+ content = f.read().strip()
|
|
|
|
|
+ print(f" {content}")
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f" 读取失败: {e}")
|
|
|
|
|
+ else:
|
|
|
|
|
+ print(" 📅 Crontab文件不存在")
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def show_files():
|
|
|
|
|
+ """显示输出文件"""
|
|
|
|
|
+ print("📁 输出文件:")
|
|
|
|
|
+
|
|
|
|
|
+ output_dir = Path("/app/output")
|
|
|
|
|
+ if not output_dir.exists():
|
|
|
|
|
+ print(" 📭 输出目录不存在")
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ # 显示最近的文件
|
|
|
|
|
+ date_dirs = sorted([d for d in output_dir.iterdir() if d.is_dir()], reverse=True)
|
|
|
|
|
+
|
|
|
|
|
+ if not date_dirs:
|
|
|
|
|
+ print(" 📭 输出目录为空")
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ # 显示最近2天的文件
|
|
|
|
|
+ for date_dir in date_dirs[:2]:
|
|
|
|
|
+ print(f" 📅 {date_dir.name}:")
|
|
|
|
|
+ for subdir in ["html", "txt"]:
|
|
|
|
|
+ sub_path = date_dir / subdir
|
|
|
|
|
+ if sub_path.exists():
|
|
|
|
|
+ files = list(sub_path.glob("*"))
|
|
|
|
|
+ if files:
|
|
|
|
|
+ recent_files = sorted(
|
|
|
|
|
+ files, key=lambda x: x.stat().st_mtime, reverse=True
|
|
|
|
|
+ )[:3]
|
|
|
|
|
+ print(f" 📂 {subdir}: {len(files)} 个文件")
|
|
|
|
|
+ for file in recent_files:
|
|
|
|
|
+ mtime = time.ctime(file.stat().st_mtime)
|
|
|
|
|
+ size_kb = file.stat().st_size // 1024
|
|
|
|
|
+ print(
|
|
|
|
|
+ f" 📄 {file.name} ({size_kb}KB, {mtime.split()[3][:5]})"
|
|
|
|
|
+ )
|
|
|
|
|
+ else:
|
|
|
|
|
+ print(f" 📂 {subdir}: 空")
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def show_logs():
|
|
|
|
|
+ """显示实时日志"""
|
|
|
|
|
+ print("📋 实时日志 (按 Ctrl+C 退出):")
|
|
|
|
|
+ print("💡 提示: 这将显示 PID 1 进程的输出")
|
|
|
|
|
+ try:
|
|
|
|
|
+ # 尝试多种方法查看日志
|
|
|
|
|
+ log_files = [
|
|
|
|
|
+ "/proc/1/fd/1", # PID 1 的标准输出
|
|
|
|
|
+ "/proc/1/fd/2", # PID 1 的标准错误
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ for log_file in log_files:
|
|
|
|
|
+ if Path(log_file).exists():
|
|
|
|
|
+ print(f"📄 尝试读取: {log_file}")
|
|
|
|
|
+ subprocess.run(["tail", "-f", log_file], check=True)
|
|
|
|
|
+ break
|
|
|
|
|
+ else:
|
|
|
|
|
+ print("📋 无法找到标准日志文件,建议使用: docker logs trend-radar")
|
|
|
|
|
+
|
|
|
|
|
+ except KeyboardInterrupt:
|
|
|
|
|
+ print("\n👋 退出日志查看")
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f"❌ 查看日志失败: {e}")
|
|
|
|
|
+ print("💡 建议使用: docker logs trend-radar")
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def restart_supercronic():
|
|
|
|
|
+ """重启supercronic进程"""
|
|
|
|
|
+ print("🔄 重启supercronic...")
|
|
|
|
|
+ print("⚠️ 注意: supercronic 是 PID 1,无法直接重启")
|
|
|
|
|
+
|
|
|
|
|
+ # 检查当前 PID 1
|
|
|
|
|
+ try:
|
|
|
|
|
+ with open('/proc/1/cmdline', 'r') as f:
|
|
|
|
|
+ pid1_cmdline = f.read().replace('\x00', ' ').strip()
|
|
|
|
|
+ print(f" 🔍 当前 PID 1: {pid1_cmdline}")
|
|
|
|
|
+
|
|
|
|
|
+ if "supercronic" in pid1_cmdline.lower():
|
|
|
|
|
+ print(" ✅ PID 1 是 supercronic")
|
|
|
|
|
+ print(" 💡 要重启 supercronic,需要重启整个容器:")
|
|
|
|
|
+ print(" docker restart trend-radar")
|
|
|
|
|
+ else:
|
|
|
|
|
+ print(" ❌ PID 1 不是 supercronic,这是异常状态")
|
|
|
|
|
+ print(" 💡 建议重启容器以修复问题:")
|
|
|
|
|
+ print(" docker restart trend-radar")
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f" ❌ 无法检查 PID 1: {e}")
|
|
|
|
|
+ print(" 💡 建议重启容器: docker restart trend-radar")
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def show_help():
|
|
|
|
|
+ """显示帮助信息"""
|
|
|
|
|
+ help_text = """
|
|
|
|
|
+🐳 TrendRadar 容器管理工具
|
|
|
|
|
+
|
|
|
|
|
+📋 命令列表:
|
|
|
|
|
+ run - 手动执行一次爬虫
|
|
|
|
|
+ status - 显示容器运行状态
|
|
|
|
|
+ config - 显示当前配置
|
|
|
|
|
+ files - 显示输出文件
|
|
|
|
|
+ logs - 实时查看日志
|
|
|
|
|
+ restart - 重启说明
|
|
|
|
|
+ help - 显示此帮助
|
|
|
|
|
+
|
|
|
|
|
+📖 使用示例:
|
|
|
|
|
+ # 在容器中执行
|
|
|
|
|
+ python manage.py run
|
|
|
|
|
+ python manage.py status
|
|
|
|
|
+ python manage.py logs
|
|
|
|
|
+
|
|
|
|
|
+ # 在宿主机执行
|
|
|
|
|
+ docker exec -it trend-radar python manage.py run
|
|
|
|
|
+ docker exec -it trend-radar python manage.py status
|
|
|
|
|
+ docker logs trend-radar
|
|
|
|
|
+
|
|
|
|
|
+💡 常用操作指南:
|
|
|
|
|
+ 1. 检查运行状态: status
|
|
|
|
|
+ - 查看 supercronic 是否为 PID 1
|
|
|
|
|
+ - 检查配置文件和关键文件
|
|
|
|
|
+ - 查看 cron 调度设置
|
|
|
|
|
+
|
|
|
|
|
+ 2. 手动执行测试: run
|
|
|
|
|
+ - 立即执行一次新闻爬取
|
|
|
|
|
+ - 测试程序是否正常工作
|
|
|
|
|
+
|
|
|
|
|
+ 3. 查看日志: logs
|
|
|
|
|
+ - 实时监控运行情况
|
|
|
|
|
+ - 也可使用: docker logs trend-radar
|
|
|
|
|
+
|
|
|
|
|
+ 4. 重启服务: restart
|
|
|
|
|
+ - 由于 supercronic 是 PID 1,需要重启整个容器
|
|
|
|
|
+ - 使用: docker restart trend-radar
|
|
|
|
|
+"""
|
|
|
|
|
+ print(help_text)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def main():
|
|
|
|
|
+ if len(sys.argv) < 2:
|
|
|
|
|
+ show_help()
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ command = sys.argv[1]
|
|
|
|
|
+ commands = {
|
|
|
|
|
+ "run": manual_run,
|
|
|
|
|
+ "status": show_status,
|
|
|
|
|
+ "config": show_config,
|
|
|
|
|
+ "files": show_files,
|
|
|
|
|
+ "logs": show_logs,
|
|
|
|
|
+ "restart": restart_supercronic,
|
|
|
|
|
+ "help": show_help,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if command in commands:
|
|
|
|
|
+ try:
|
|
|
|
|
+ commands[command]()
|
|
|
|
|
+ except KeyboardInterrupt:
|
|
|
|
|
+ print("\n👋 操作已取消")
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f"❌ 执行出错: {e}")
|
|
|
|
|
+ else:
|
|
|
|
|
+ print(f"❌ 未知命令: {command}")
|
|
|
|
|
+ print("运行 'python manage.py help' 查看可用命令")
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+if __name__ == "__main__":
|
|
|
|
|
+ main()
|