Files
Lottery/web_executor.py
T
vincent 13a259b0f8 chore: initial commit — existing lottoData codebase
Files:
- lottery.py (1189 lines) — DoubleColorBallGenerator core engine
- fetch_data.py (131 lines) — history data fetcher from 55128.cn
- web_executor.py (216 lines) — data fetch Web console (Flask :5000)
- app.py (505 lines) — number generation Web service (Flask :8085)
- index.html (1171 lines) — frontend SPA
- web_console.html (323 lines) — fetch console frontend
- deploy/ — systemd service + cron script + logs

BIZ-74 architecture review baseline
2026-07-03 16:39:21 +08:00

217 lines
6.3 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
双色球数据抓取 Web 服务
提供 Web 界面执行抓取任务和查看实时结果
监听 0.0.0.0,支持局域网访问
"""
from flask import Flask, send_from_directory, jsonify
import subprocess
import os
import json
from datetime import datetime
import threading
app = Flask(__name__)
# 脚本路径和输出文件
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
SCRIPT_PATH = os.path.join(SCRIPT_DIR, "fetch_data.py")
OUTPUT_FILE = os.path.join(SCRIPT_DIR, "双色球历史数据.xlsx")
STATUS_FILE = os.path.join(SCRIPT_DIR, ".fetch_status.json")
# 全局状态
execution_status = {
"is_running": False,
"last_update": None,
"last_record_count": 0,
"last_error": None
}
# 状态锁
status_lock = threading.Lock()
def load_status():
"""从文件加载状态"""
global execution_status
if os.path.exists(STATUS_FILE):
try:
with open(STATUS_FILE, 'r', encoding='utf-8') as f:
execution_status = json.load(f)
except:
pass
def save_status():
"""保存状态到文件"""
with status_lock:
with open(STATUS_FILE, 'w', encoding='utf-8') as f:
json.dump(execution_status, f, ensure_ascii=False, indent=2)
@app.route('/')
def index():
"""首页 - Web 控制台"""
return send_from_directory(SCRIPT_DIR, 'web_console.html')
@app.route('/api/status')
def api_status():
"""获取当前执行状态"""
with status_lock:
return jsonify({
"isRunning": execution_status.get("is_running", False),
"lastUpdate": execution_status.get("last_update"),
"recordCount": execution_status.get("last_record_count", 0),
"lastError": execution_status.get("last_error")
})
@app.route('/api/execute', methods=['POST'])
def api_execute():
"""执行抓取脚本"""
global execution_status
with status_lock:
if execution_status.get("is_running", False):
return jsonify({
"success": False,
"error": "任务正在执行中,请稍后再试"
}), 409
# 启动执行线程
def run_script():
global execution_status
with status_lock:
execution_status["is_running"] = True
execution_status["last_error"] = None
save_status()
try:
print(f"[{datetime.now()}] 开始执行抓取脚本...")
# 执行 Python 脚本
result = subprocess.run(
["python3", SCRIPT_PATH],
capture_output=True,
text=True,
timeout=300
)
if result.returncode == 0:
# 解析输出,获取记录数
record_count = 0
for line in result.stdout.split('\n'):
if '共保存' in line and '条记录' in line:
try:
record_count = int(line.split('共保存')[1].split('条记录')[0].strip())
except:
pass
elif '成功解析' in line and '条数据' in line:
try:
record_count = int(line.split('成功解析')[1].split('条数据')[0].strip())
except:
pass
with status_lock:
execution_status["last_update"] = datetime.now().isoformat()
execution_status["last_record_count"] = record_count
execution_status["is_running"] = False
save_status()
print(f"✅ 执行成功,共抓取 {record_count} 条数据")
else:
error_msg = result.stderr or f"脚本执行失败,返回码:{result.returncode}"
with status_lock:
execution_status["last_error"] = error_msg
execution_status["is_running"] = False
save_status()
print(f"❌ {error_msg}")
except subprocess.TimeoutExpired:
error_msg = "脚本执行超时(超过 5 分钟)"
with status_lock:
execution_status["last_error"] = error_msg
execution_status["is_running"] = False
save_status()
print(f"❌ {error_msg}")
except Exception as e:
error_msg = f"执行异常:{str(e)}"
with status_lock:
execution_status["last_error"] = error_msg
execution_status["is_running"] = False
save_status()
print(f"❌ {error_msg}")
# 在后台线程执行
thread = threading.Thread(target=run_script, daemon=True)
thread.start()
return jsonify({
"success": True,
"message": "任务已启动,正在执行中..."
})
def check_dependencies():
"""检查依赖"""
missing = []
try:
import flask
except ImportError:
missing.append("flask")
try:
import requests
except ImportError:
missing.append("requests")
try:
import bs4
except ImportError:
missing.append("beautifulsoup4")
try:
import pandas
except ImportError:
missing.append("pandas")
try:
import openpyxl
except ImportError:
missing.append("openpyxl")
if missing:
print(f"❌ 缺少依赖包:{', '.join(missing)}")
print(f" 请运行:pip3 install {' '.join(missing)}")
return False
print("✅ 所有依赖已安装")
return True
if __name__ == "__main__":
print("=" * 60)
print("双色球数据抓取 Web 服务")
print("=" * 60)
if not check_dependencies():
exit(1)
load_status()
print(f"\n📂 脚本路径:{SCRIPT_PATH}")
print(f"📁 输出文件:{OUTPUT_FILE}")
print(f"\n🌐 服务启动中...")
print(f" 监听地址:http://0.0.0.0:5000")
print(f" 访问方式:局域网内任意设备访问 http://<本机 IP>:5000")
print(f"\n✅ 服务就绪!")
print("=" * 60)
app.run(host='0.0.0.0', port=5000, debug=False, threaded=True)