# GDELT 实时冲突服务 + 新闻爬虫

## 数据来源梳理

### 1. GDELT Project (gdelt_events)

| 项目 | 说明 |
|------|------|
| API | `https://api.gdeltproject.org/api/v2/doc/doc` |
| 查询 | `query=United States Iran military`（可配 `GDELT_QUERY`） |
| 模式 | `mode=ArtList`，`format=json`，`maxrecords=30` |
| 时间范围 | **未指定时默认最近 3 个月**，按相关性排序，易返回较旧文章 |
| 更新频率 | GDELT 约 15 分钟级，爬虫 60 秒拉一次 |

**数据偏老原因**：未传 `timespan` 和 `sort=datedesc`，API 返回 3 个月内“最相关”文章，不保证最新。

### 2. RSS 新闻 (situation_update) — 主事件脉络来源

| 项目 | 说明 |
|------|------|
| 源 | 多国主流媒体：美(Reuters/NYT)、英(BBC)、法(France 24)、俄(TASS/RT)、中(Xinhua/CGTN)、伊(Press TV)、卡塔尔(Al Jazeera) |
| 过滤 | 标题/摘要需含 `KEYWORDS` 之一（iran、usa、strike、military 等） |
| 更新 | 爬虫 45 秒拉一次（`RSS_INTERVAL_SEC`），优先保证事件脉络 |
| 优先级 | 启动时先拉 RSS，再拉 GDELT |

**GDELT 无法访问时**：设置 `GDELT_DISABLED=1`，仅用 RSS 新闻即可维持事件脉络。部分境外源可能受网络限制。

### 3. AI 新闻清洗与分类（可选）

- **清洗**：`cleaner_ai.py` 用 Ollama 提炼新闻为简洁摘要，供面板展示
- **分类**：`parser_ai.py` 用 Ollama 替代规则做 category/severity 判定
- 需先安装并运行 Ollama：`ollama run llama3.1`
- 环境变量：`OLLAMA_MODEL=llama3.1`、`PARSER_AI_DISABLED=1`、`CLEANER_AI_DISABLED=1`（禁用对应 AI）

---

**事件脉络可实时更新**：爬虫抓取后 → 写入 SQLite → 调用 Node 通知 → WebSocket 广播 → 前端自动刷新。

## 依赖

```bash
pip install -r requirements.txt
```

新增 `deep-translator`：GDELT 与 RSS 新闻入库前自动翻译为中文。

## 运行（需同时启动 3 个服务）

| 终端 | 命令 | 说明 |
|------|------|------|
| 1 | `npm run api` | Node API + WebSocket（必须） |
| 2 | `npm run gdelt` | GDELT + RSS 爬虫（**事件脉络数据来源**） |
| 3 | `npm run dev` | 前端开发 |

**事件脉络不更新时**：多半是未启动 `npm run gdelt`。只跑 `npm run api` 时，事件脉络会显示空或仅有缓存。

## 数据流

```
GDELT API → 抓取(60s) → SQLite (gdelt_events, conflict_stats) → POST /api/crawler/notify
                                                                    ↓
                                              Node 更新 situation.updated_at + WebSocket 广播
                                                                    ↓
                                                              前端实时展示
```

## 配置

环境变量：

- `DB_PATH`: SQLite 路径，默认 `../server/data.db`
- `API_BASE`: Node API 地址，默认 `http://localhost:3001`
- `GDELT_QUERY`: 搜索关键词，默认 `United States Iran military`
- `GDELT_MAX_RECORDS`: 最大条数，默认 30
- `GDELT_TIMESPAN`: 时间范围，`1h` / `1d` / `1week`，默认 `1d`（近日资讯）
- `GDELT_DISABLED`: 设为 `1` 则跳过 GDELT，仅用 RSS 新闻（GDELT 无法访问时用）
- `FETCH_INTERVAL_SEC`: GDELT 抓取间隔（秒），默认 60
- `RSS_INTERVAL_SEC`: RSS 抓取间隔（秒），默认 45（优先保证事件脉络）
- `OLLAMA_MODEL`: AI 分类模型，默认 `llama3.1`
- `PARSER_AI_DISABLED`: 设为 `1` 则禁用 AI 分类，仅用规则
- `CLEANER_AI_DISABLED`: 设为 `1` 则禁用 AI 清洗，仅用规则截断

## 冲突强度 (impact_score)

| 分数 | 地图效果   |
|------|------------|
| 1–3  | 绿色点     |
| 4–6  | 橙色闪烁   |
| 7–10 | 红色脉冲扩散 |

## API

- `GET http://localhost:8000/events`：返回事件列表与冲突统计（Python 服务直连）
- `GET http://localhost:3001/api/events`：从 Node 读取（推荐，含 WebSocket 同步）

## 故障排查

| 现象 | 可能原因 | 排查 |
|------|----------|------|
| 事件脉络始终为空 | 未启动 GDELT 爬虫 | 另开终端运行 `npm run gdelt`，观察是否有 `GDELT 更新 X 条事件` 输出 |
| 事件脉络不刷新 | WebSocket 未连上 | 确认 `npm run api` 已启动，前端需通过 `npm run dev` 访问（Vite 会代理 /ws） |
| GDELT 抓取失败 | 系统代理超时 / ProxyError | 爬虫默认直连，不走代理；若需代理请设 `CRAWLER_USE_PROXY=1` |
| GDELT 抓取失败 | 网络 / GDELT API 限流 | 检查 Python 终端报错；GDELT 在国外，国内网络可能较慢或超时 |
| 新闻条数为 0 | RSS 源被墙或关键词不匹配 | 检查 crawler/config.py 中 RSS_FEEDS、KEYWORDS；国内需代理 |
| **返回数据偏老** | GDELT 默认 3 个月内按相关性 | 设置 `GDELT_TIMESPAN=1d` 限制为近日；加 `sort=datedesc` 最新优先 |
