# -*- coding: utf-8 -*- """ 每日热榜 Skill - 数据存储模块 功能: - 自动保存每日热榜数据 - 查询历史热榜记录 - 管理数据文件 """ import json import os from datetime import datetime, timedelta from typing import Dict, List, Optional, Any from config import config class DataStorage: """数据存储类""" def __init__(self): self.data_dir = config.data_dir self.auto_save = config.auto_save os.makedirs(self.data_dir, exist_ok=True) def save_hot_list(self, source_id: str, data: Dict[str, Any]) -> bool: """ 保存热榜数据到文件 Args: source_id: 热榜源 ID(如 weibo, zhihu) data: 热榜数据字典 Returns: 是否保存成功 """ if not self.auto_save: return False try: # 获取今日日期的文件路径 file_path = config.get_data_path(source_id) # 构建存储结构 storage_data = { "source_id": source_id, "save_time": datetime.now().isoformat(), "update_time": data.get("update_time", ""), "total": data.get("total", 0), "data": data.get("data", []) } # 读取现有数据(如果有) existing_data = [] if os.path.exists(file_path): with open(file_path, 'r', encoding='utf-8') as f: try: existing_data = json.load(f) if not isinstance(existing_data, list): existing_data = [] except: existing_data = [] # 添加新数据到列表 existing_data.append(storage_data) # 保存到文件 with open(file_path, 'w', encoding='utf-8') as f: json.dump(existing_data, f, ensure_ascii=False, indent=2) return True except Exception as e: print(f"[DataStorage] 保存失败: {e}") return False def load_history(self, source_id: str = "", days: int = 7) -> Dict[str, List[Dict]]: """ 加载历史热榜数据 Args: source_id: 热榜源 ID(空则加载所有源) days: 加载最近几天的数据 Returns: 按日期组织的热榜数据 """ result = {} if source_id: # 加载指定源的历史数据 source_dir = os.path.join(self.data_dir, source_id) if not os.path.exists(source_dir): return {} for i in range(days): date_str = (datetime.now() - timedelta(days=i)).strftime("%Y-%m-%d") file_path = os.path.join(source_dir, f"{date_str}.json") if os.path.exists(file_path): with open(file_path, 'r', encoding='utf-8') as f: try: data = json.load(f) result[date_str] = data except: continue else: # 加载所有源的历史数据 if not os.path.exists(self.data_dir): return {} for source in os.listdir(self.data_dir): source_dir = os.path.join(self.data_dir, source) if not os.path.isdir(source_dir): continue result[source] = {} for i in range(days): date_str = (datetime.now() - timedelta(days=i)).strftime("%Y-%m-%d") file_path = os.path.join(source_dir, f"{date_str}.json") if os.path.exists(file_path): with open(file_path, 'r', encoding='utf-8') as f: try: data = json.load(f) result[source][date_str] = data except: continue return result def get_saved_dates(self, source_id: str) -> List[str]: """ 获取指定热榜源已保存的日期列表 Args: source_id: 热榜源 ID Returns: 已保存的日期列表(降序) """ dates = [] source_dir = os.path.join(self.data_dir, source_id) if not os.path.exists(source_dir): return [] for filename in os.listdir(source_dir): if filename.endswith('.json'): date_str = filename.replace('.json', '') dates.append(date_str) return sorted(dates, reverse=True) def get_old_data_files(self, days: int = 7) -> List[Dict[str, str]]: """ 获取指定天数之前的旧数据文件列表 Args: days: 天数阈值(默认7天) Returns: 旧文件列表 [{source_id, date_str, file_path}] """ from datetime import datetime, timedelta old_files = [] cutoff_date = datetime.now() - timedelta(days=days) if not os.path.exists(self.data_dir): return [] for source in os.listdir(self.data_dir): source_dir = os.path.join(self.data_dir, source) if not os.path.isdir(source_dir): continue for filename in os.listdir(source_dir): if not filename.endswith('.json'): continue try: file_date = datetime.strptime(filename.replace('.json', ''), "%Y-%m-%d") if file_date < cutoff_date: old_files.append({ "source_id": source, "date_str": filename.replace('.json', ''), "file_path": os.path.join(source_dir, filename) }) except: continue return old_files def cleanup_old_files(self, files: List[Dict[str, str]]) -> int: """ 删除指定的旧文件 Args: files: 旧文件列表 Returns: 删除的文件数量 """ deleted_count = 0 for f in files: try: if os.path.exists(f["file_path"]): os.remove(f["file_path"]) deleted_count += 1 except Exception as e: print(f"[DataStorage] 删除失败 {f['file_path']}: {e}") return deleted_count def load_hot_list(self, source_id: str, date_str: str = "") -> Optional[List[Dict]]: """ 加载指定日期的热榜数据 Args: source_id: 热榜源 ID date_str: 日期(默认今天) Returns: 热榜数据列表 """ if not date_str: date_str = datetime.now().strftime("%Y-%m-%d") file_path = config.get_data_path(source_id, date_str) if not os.path.exists(file_path): return None with open(file_path, 'r', encoding='utf-8') as f: try: data = json.load(f) # 返回当天的最后一条记录 if isinstance(data, list) and len(data) > 0: return data[-1].get("data", []) except: pass return None def list_all_data(self) -> Dict[str, int]: """ 列出所有已保存的热榜数据统计 Returns: 源ID -> 保存记录数 """ stats = {} if not os.path.exists(self.data_dir): return {} for source in os.listdir(self.data_dir): source_dir = os.path.join(self.data_dir, source) if not os.path.isdir(source_dir): continue count = 0 for filename in os.listdir(source_dir): if filename.endswith('.json'): count += 1 if count > 0: stats[source] = count return stats def clear_old_data(self, keep_days: int = 30) -> int: """ 清理旧数据 Args: keep_days: 保留最近几天的数据 Returns: 删除的文件数量 """ deleted_count = 0 cutoff_date = datetime.now() - timedelta(days=keep_days) if not os.path.exists(self.data_dir): return 0 for source in os.listdir(self.data_dir): source_dir = os.path.join(self.data_dir, source) if not os.path.isdir(source_dir): continue for filename in os.listdir(source_dir): if not filename.endswith('.json'): continue try: file_date = datetime.strptime(filename.replace('.json', ''), "%Y-%m-%d") if file_date < cutoff_date: file_path = os.path.join(source_dir, filename) os.remove(file_path) deleted_count += 1 except: continue return deleted_count # 全局存储实例 storage = DataStorage() def auto_save_hot_list(source_id: str, data: Dict[str, Any]) -> bool: """ 自动保存热榜数据的便捷函数 在获取热榜数据后调用此函数保存数据 """ return storage.save_hot_list(source_id, data) if __name__ == "__main__": # 测试存储功能 print("📊 每日热榜数据存储管理") print("=" * 50) # 列出所有数据 stats = storage.list_all_data() if stats: print("\n已保存的热榜数据:") for source, count in stats.items(): print(f" • {source}: {count} 条记录") else: print("\n暂无保存的数据") # 列出已保存的日期 print("\n已保存的日期(微博):") dates = storage.get_saved_dates("weibo") if dates: for date in dates[:7]: print(f" • {date}") else: print(" 暂无数据")