7.0 KiB
7.0 KiB
爬虫逻辑梳理与数据校验
一、两条入口,数据流不同
1. 入口 A:npm run crawler(main.py)
- 流程:RSS 抓取 → 关键词过滤 → 分类/严重度 → 直接写 situation_update → 通知 API
- 不经过:翻译、news_content、AI 提取(战损/基地等)
- 写入表:
situation_update、situation.updated_at - 用途:轻量、只给「事件脉络」喂新条目,不更新战损/基地/报复指数
RSS_FEEDS → fetch_all() → KEYWORDS 过滤 → parser_ai.classify_and_severity
→ write_updates(items) → situation_update INSERT + situation 表 touch
→ notify_api()
2. 入口 B:npm run gdelt(realtime_conflict_service.py)
- 流程:RSS 抓取 → 翻译 → 清洗 → news_content 去重 → situation_update → AI 提取 → db_merge → GDELT 事件(可选)→ 通知 API
- 写入表:
news_content、situation_update、situation;提取后还有combat_losses、key_location、retaliation_*、wall_street_trend等 - 用途:完整管线,前端「战损 / 军事基地 / 报复 / 美股」等数据都依赖这条
RSS → fetch_all() → translate_to_chinese → cleaner_ai → save_and_dedup → news_content
→ write_updates(new_items) → situation_update
→ _extract_and_merge_panel_data(new_items) → extract_from_news() → db_merge.merge()
→ (可选) fetch_gdelt_events() → gdelt_events, conflict_stats
→ _notify_node()
结论:要检查「抓回的数据是否有效」且包含战损/基地等,应跑 入口 B(gdelt 服务);若只关心事件脉络条数,可看入口 A。
二、入口 B 逐步拆解(用于逐段校验)
2.1 RSS 抓取与过滤
| 步骤 | 位置 | 说明 |
|---|---|---|
| 源列表 | config.RSS_FEEDS |
多国媒体 RSS,见 config.py |
| 抓取 | scrapers.rss_scraper.fetch_all() |
feedparser,单源超时 10s |
| 过滤 | _matches_keywords(text) |
标题+摘要 至少命中 config.KEYWORDS 中一个才保留 |
| 去重 | (title[:80], link) |
同一条不重复加入当次列表 |
| 分类 | parser_ai.classify_and_severity(text) |
得到 category、severity(Ollama 或规则) |
校验:npm run crawler:test 看本次抓到的条数;若为 0,查网络或放宽/检查 KEYWORDS。
2.2 翻译与清洗(仅入口 B)
| 步骤 | 位置 | 说明 |
|---|---|---|
| 翻译 | translate_utils.translate_to_chinese() |
标题/摘要译成中文(依赖配置) |
| 清洗 | cleaner_ai.clean_news_for_panel() |
截断、清理;ensure_category / ensure_severity 合法化 |
2.3 落库:news_content(去重)与 situation_update
| 步骤 | 位置 | 说明 |
|---|---|---|
| 去重 | news_storage.save_and_dedup(items) |
按 content_hash(title, summary, url) 判重,只插入新记录 |
| 表 | news_content |
id, content_hash, title, summary, url, source, published_at, category, severity |
| 表 | situation_update |
仅对 去重后的 new_items 调用 write_updates(),供前端「事件脉络」 |
校验:
news_content:SELECT COUNT(*), MAX(published_at) FROM news_contentsituation_update:SELECT COUNT(*), MAX(timestamp) FROM situation_update- 服务状态:
GET http://localhost:8000/crawler/status看last_fetch_items/last_fetch_inserted/last_fetch_error
2.4 AI 提取与 db_merge(战损 / 基地 / 报复等)
| 步骤 | 位置 | 说明 |
|---|---|---|
| 输入 | _extract_and_merge_panel_data(new_items) |
仅处理本次 新增 的 new_items,前 limit 条(DashScope 10 条,规则 25 条,Ollama 10 条) |
| 文本 | 每条 title + " " + summary,长度 < 20 跳过 |
|
| 提取器选择 | 环境变量 | DASHSCOPE_API_KEY → extractor_dashscope;CLEANER_AI_DISABLED=1 → extractor_rules;否则 extractor_ai(Ollama) |
| 输出结构 | 见 panel_schema / 各 extractor | situation_update?, combat_losses_delta?, retaliation?, wall_street?, key_location_updates? |
| 合并 | db_merge.merge(extracted) |
见下表 |
merge 映射概要:
| 提取字段 | 写入表/逻辑 |
|---|---|
| situation_update | situation_update 表 INSERT(id 为 hash) |
| combat_losses_delta | combat_losses 表,按 side 增量叠加 |
| retaliation | retaliation_current 替换 + retaliation_history 追加 |
| wall_street | wall_street_trend 表 INSERT |
| key_location_updates | key_location 表 UPDATE status/damage_level(name LIKE 关键词) |
校验:
- 战损:
SELECT * FROM combat_losses - 基地:
SELECT id, name, side, status, damage_level FROM key_location WHERE status != 'operational' OR damage_level > 0 - 报复:
SELECT * FROM retaliation_current与retaliation_history最近几条 - 事件脉络:
SELECT id, timestamp, category, summary, severity FROM situation_update ORDER BY timestamp DESC LIMIT 20
2.5 GDELT(可选)
GDELT_DISABLED=1时跳过 GDELT,仅用 RSS;可用_rss_to_gdelt_fallback()用 RSS 标题生成 gdelt_events。- 未禁用时:
fetch_gdelt_events()拉 GDELT → 写gdelt_events、conflict_stats。
校验:SELECT COUNT(*), MAX(event_time) FROM gdelt_events;SELECT * FROM conflict_stats WHERE id=1。
三、如何检查「抓回的数据是否有效」
-
确认跑的入口
- 只跑
npm run crawler:只有 situation_update 会有新数据,战损/基地不会变。 - 跑
npm run gdelt且服务常驻:才会既有 situation_update,又有 combat_losses、key_location 等。
- 只跑
-
看 DB 与 API
- 同上:查
news_content、situation_update、combat_losses、key_location、retaliation_*、gdelt_events、conflict_stats。 - 前端数据来源:
GET /api/situation(见 server/situationData.js),对照上述表即可。
- 同上:查
-
看提取是否触发
- 若
combat_losses/key_location一直不更新:确认是入口 B、有 new_items、提取器未报错;可对单条新闻跑extract_from_news(text)看是否产出 combat_losses_delta / key_location_updates。
- 若
-
重跑历史提取(补数据)
POST http://localhost:8000/crawler/backfill:用当前 situation_update 最近 50 条重新做一次提取并 merge,可用来修历史未提取的数据。
四、配置与环境变量(与数据有效性相关)
| 变量 | 作用 |
|---|---|
| DB_PATH | 与 server 共用的 SQLite 路径,必须一致 |
| API_BASE | 通知 Node 的地址,merge 后通知前端 |
| DASHSCOPE_API_KEY | 有则用 DashScope 提取;无则用 Ollama 或规则 |
| CLEANER_AI_DISABLED=1 | 用规则提取(extractor_rules),不用 Ollama |
| GDELT_DISABLED=1 | 不用 GDELT,仅 RSS;RSS 可转 gdelt_events 占位 |
| CRAWL_INTERVAL | main.py 抓取间隔(秒) |
| RSS_INTERVAL_SEC / FETCH_INTERVAL_SEC | realtime 服务里 RSS / GDELT 间隔 |
按上述顺序对照「入口 → RSS → 去重 → situation_update → 提取 → merge → 表」即可逐段检查爬虫抓回的数据是否有效。