137 lines
4.0 KiB
Python
137 lines
4.0 KiB
Python
"""Use DeepSeek to summarize top news into categorized Chinese briefs (5 per category)."""
|
||
import json, os, requests
|
||
from datetime import datetime, timezone, timedelta
|
||
|
||
API_KEY = "sk-bbca4a0380d549389f0d27cdea0b5228"
|
||
API_URL = "https://api.deepseek.com/v1/chat/completions"
|
||
MODEL = "deepseek-v4-flash"
|
||
|
||
NEWS_FILE = "/var/www/nav/data/news.json"
|
||
OUTPUT = "/var/www/nav/data/news_summary.json"
|
||
tz = timezone(timedelta(hours=8))
|
||
|
||
|
||
def load_raw_news():
|
||
if not os.path.exists(NEWS_FILE):
|
||
return []
|
||
with open(NEWS_FILE) as f:
|
||
data = json.load(f)
|
||
return data.get("items", [])
|
||
|
||
|
||
def summarize(items):
|
||
news_text = ""
|
||
for i, item in enumerate(items[:30], 1):
|
||
title = item.get("title", "")
|
||
summary = item.get("summary", "")[:150].replace("<", "").replace(">", "")
|
||
source = item.get("source", "")
|
||
news_text += f"{i}. [{source}] {title}\n {summary}\n\n"
|
||
|
||
prompt = f"""你是一个新闻整理助手。以下是来自BBC世界新闻、BBC中文、新浪新闻、涌报、Hacker News的最新资讯。
|
||
|
||
请从以上内容中精选出最重要的新闻,按以下分类组织(每个分类最多5条):
|
||
|
||
分类要求:
|
||
- 国际要闻:全球政治、外交、军事
|
||
- 科技前沿:AI、软件、硬件、互联网
|
||
- 财经动向:市场、经济、商业
|
||
- 中国热点:中国相关新闻
|
||
- 社会生活:健康、环境、文化、民生
|
||
|
||
每条新闻格式:
|
||
## 小标题(10字以内概括核心)
|
||
|
||
正文(200-300字中文精炼摘要,包含核心事实和背景)
|
||
|
||
输出格式为JSON:
|
||
{{
|
||
"categories": [
|
||
{{
|
||
"name": "国际要闻",
|
||
"items": [
|
||
{{"rank": 1, "subtitle": "小标题", "title": "原标题", "source": "BBC World", "summary": "正文..."}},
|
||
...
|
||
]
|
||
}},
|
||
...
|
||
]
|
||
}}
|
||
|
||
注意:
|
||
- 每个分类最多5条
|
||
- 小标题要精炼抓人
|
||
- 摘要要有信息量,不要空话
|
||
- 只输出JSON,不要其他内容
|
||
|
||
原始新闻:
|
||
{news_text}"""
|
||
|
||
resp = requests.post(
|
||
API_URL,
|
||
headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
|
||
json={
|
||
"model": MODEL,
|
||
"messages": [{"role": "user", "content": prompt}],
|
||
"temperature": 0.4,
|
||
"max_tokens": 8000,
|
||
},
|
||
timeout=180,
|
||
)
|
||
|
||
if resp.status_code != 200:
|
||
print(f"API error: {resp.status_code} {resp.text[:200]}")
|
||
return None
|
||
|
||
content = resp.json()["choices"][0]["message"]["content"]
|
||
content = content.strip()
|
||
if content.startswith("```"):
|
||
lines = content.split("\n")
|
||
content = "\n".join(lines[1:-1]) if lines[-1].strip() == "```" else "\n".join(lines[1:])
|
||
|
||
try:
|
||
result = json.loads(content)
|
||
except:
|
||
# Try to fix common JSON issues
|
||
content = content.strip()
|
||
# Remove trailing commas before closing brackets
|
||
import re
|
||
content = re.sub(r',\s*}', '}', content)
|
||
content = re.sub(r',\s*]', ']', content)
|
||
try:
|
||
result = json.loads(content)
|
||
except:
|
||
print(f"JSON parse failed, len={len(content)}")
|
||
print(f"First 800 chars: {content[:800]}")
|
||
return None
|
||
|
||
return result
|
||
|
||
|
||
if __name__ == "__main__":
|
||
now = datetime.now(tz).strftime("%Y-%m-%d %H:%M")
|
||
items = load_raw_news()
|
||
if not items:
|
||
print("No news data")
|
||
exit(0)
|
||
|
||
print(f"Loaded {len(items)} raw news items. AI summarizing by category...")
|
||
data = summarize(items)
|
||
|
||
if not data:
|
||
print("Summarization failed")
|
||
exit(1)
|
||
|
||
result = {
|
||
"updated_at": now,
|
||
"categories": data.get("categories", []),
|
||
}
|
||
|
||
total = sum(len(c["items"]) for c in result["categories"])
|
||
os.makedirs(os.path.dirname(OUTPUT), exist_ok=True)
|
||
with open(OUTPUT, "w", encoding="utf-8") as f:
|
||
json.dump(result, f, ensure_ascii=False, indent=2)
|
||
|
||
print(f"Written {total} articles across {len(result['categories'])} categories")
|
||
for c in result["categories"]:
|
||
print(f" {c['name']}: {len(c['items'])} items")
|