"""存储管理:index.jsonl 读写、输出目录管理""" import json import logging from pathlib import Path from crawler.models import ArticleItem, CrawlResult from crawler.utils import get_news_day logger = logging.getLogger(__name__) def write_index_jsonl(result: CrawlResult) -> Path: """将单源抓取结果写入 index.jsonl Args: result: 单源抓取结果 Returns: index 文件路径 """ today = get_news_day() out_dir = Path(f"data/raw/{result.source_id}/{today}") out_dir.mkdir(parents=True, exist_ok=True) index_path = out_dir / "index.jsonl" # 追加写入(同一天多次抓取合并) existing: set[str] = set() if index_path.exists(): with open(index_path, encoding="utf-8") as f: for line in f: line = line.strip() if not line: continue try: item = json.loads(line) existing.add(item.get("url_hash", "")) except json.JSONDecodeError: continue written = 0 with open(index_path, "a", encoding="utf-8") as f: for article in result.articles: if article.status != "success": continue if article.url_hash in existing: continue # 跳过已存在的 record = article.model_dump() f.write(json.dumps(record, ensure_ascii=False) + "\n") existing.add(article.url_hash) written += 1 logger.info("[%s] index.jsonl 写入 %d 条(跳过重复 %d 条)", result.source_id, written, len(result.articles) - written) return index_path def load_index( source_id: str, date_str: str | None = None, ) -> list[ArticleItem]: """读取指定源/日期的 index.jsonl Args: source_id: 新闻源 ID date_str: 日期字符串 YYYYMMDD,默认当前新闻日 Returns: ArticleItem 列表 """ if date_str is None: date_str = get_news_day() index_path = Path(f"data/raw/{source_id}/{date_str}/index.jsonl") if not index_path.exists(): logger.warning("index 文件不存在: %s", index_path) return [] articles: list[ArticleItem] = [] with open(index_path, encoding="utf-8") as f: for line in f: line = line.strip() if not line: continue try: data = json.loads(line) articles.append(ArticleItem(**data)) except (json.JSONDecodeError, Exception) as e: logger.warning("解析 index 行失败: %s", e) return articles