topic_extractor.py
10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
BroadTopicExtraction模块 - 话题提取器
基于DeepSeek直接提取关键词和生成新闻总结
"""
import sys
import json
import re
from pathlib import Path
from typing import List, Dict, Tuple
from openai import OpenAI
# 添加项目根目录到路径
project_root = Path(__file__).parent.parent
sys.path.append(str(project_root))
try:
import config
except ImportError:
raise ImportError("无法导入config.py配置文件")
class TopicExtractor:
"""话题提取器"""
def __init__(self):
"""初始化话题提取器"""
self.client = OpenAI(
api_key=config.DEEPSEEK_API_KEY,
base_url="https://api.deepseek.com"
)
self.model = "deepseek-chat"
def extract_keywords_and_summary(self, news_list: List[Dict], max_keywords: int = 100) -> Tuple[List[str], str]:
"""
从新闻列表中提取关键词和生成总结
Args:
news_list: 新闻列表
max_keywords: 最大关键词数量
Returns:
(关键词列表, 新闻分析总结)
"""
if not news_list:
return [], "今日暂无热点新闻"
# 构建新闻摘要文本
news_text = self._build_news_summary(news_list)
# 构建提示词
prompt = self._build_analysis_prompt(news_text, max_keywords)
try:
# 调用DeepSeek API
response = self.client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": "你是一个专业的新闻分析师,擅长从热点新闻中提取关键词和撰写分析总结。"},
{"role": "user", "content": prompt}
],
max_tokens=1500,
temperature=0.3
)
# 解析返回结果
result_text = response.choices[0].message.content
keywords, summary = self._parse_analysis_result(result_text)
print(f"成功提取 {len(keywords)} 个关键词并生成新闻总结")
return keywords[:max_keywords], summary
except Exception as e:
print(f"话题提取失败: {e}")
# 返回简单的fallback结果
fallback_keywords = self._extract_simple_keywords(news_list)
fallback_summary = f"今日共收集到 {len(news_list)} 条热点新闻,涵盖多个平台的热门话题。"
return fallback_keywords[:max_keywords], fallback_summary
def _build_news_summary(self, news_list: List[Dict]) -> str:
"""构建新闻摘要文本"""
news_items = []
for i, news in enumerate(news_list, 1):
title = news.get('title', '无标题')
source = news.get('source_platform', news.get('source', '未知'))
# 清理标题中的特殊字符
title = re.sub(r'[#@]', '', title).strip()
news_items.append(f"{i}. 【{source}】{title}")
return "\n".join(news_items)
def _build_analysis_prompt(self, news_text: str, max_keywords: int) -> str:
"""构建分析提示词"""
news_count = len(news_text.split('\n'))
prompt = f"""
请分析以下{news_count}条今日热点新闻,完成两个任务:
新闻列表:
{news_text}
任务1:提取关键词(最多{max_keywords}个)
- 提取能代表今日热点话题的关键词
- 关键词应该适合用于社交媒体平台搜索
- 优先选择热度高、讨论量大的话题
- 避免过于宽泛或过于具体的词汇
任务2:撰写新闻分析总结(150-300字)
- 简要概括今日热点新闻的主要内容
- 指出当前社会关注的重点话题方向
- 分析这些热点反映的社会现象或趋势
- 语言简洁明了,客观中性
请严格按照以下JSON格式输出:
```json
{{
"keywords": ["关键词1", "关键词2", "关键词3"],
"summary": "今日新闻分析总结内容..."
}}
```
请直接输出JSON格式的结果,不要包含其他文字说明。
"""
return prompt
def _parse_analysis_result(self, result_text: str) -> Tuple[List[str], str]:
"""解析分析结果"""
try:
# 尝试提取JSON部分
json_match = re.search(r'```json\s*(.*?)\s*```', result_text, re.DOTALL)
if json_match:
json_text = json_match.group(1)
else:
# 如果没有代码块,尝试直接解析
json_text = result_text.strip()
# 解析JSON
data = json.loads(json_text)
keywords = data.get('keywords', [])
summary = data.get('summary', '')
# 验证和清理关键词
clean_keywords = []
for keyword in keywords:
keyword = str(keyword).strip()
if keyword and len(keyword) > 1 and keyword not in clean_keywords:
clean_keywords.append(keyword)
# 验证总结
if not summary or len(summary.strip()) < 10:
summary = "今日热点新闻涵盖多个领域,反映了当前社会的多元化关注点。"
return clean_keywords, summary.strip()
except json.JSONDecodeError as e:
print(f"解析JSON失败: {e}")
print(f"原始返回: {result_text}")
# 尝试手动解析
return self._manual_parse_result(result_text)
except Exception as e:
print(f"处理分析结果失败: {e}")
return [], "分析结果处理失败,请稍后重试。"
def _manual_parse_result(self, text: str) -> Tuple[List[str], str]:
"""手动解析结果(当JSON解析失败时的后备方案)"""
print("尝试手动解析结果...")
keywords = []
summary = ""
lines = text.split('\n')
for line in lines:
line = line.strip()
if not line:
continue
# 寻找关键词
if '关键词' in line or 'keywords' in line.lower():
# 提取关键词
keyword_match = re.findall(r'[""](.*?)["""]', line)
if keyword_match:
keywords.extend(keyword_match)
else:
# 尝试其他分隔符
parts = re.split(r'[,,、]', line)
for part in parts:
clean_part = re.sub(r'[关键词::keywords\[\]"]', '', part).strip()
if clean_part and len(clean_part) > 1:
keywords.append(clean_part)
# 寻找总结
elif '总结' in line or '分析' in line or 'summary' in line.lower():
if ':' in line or ':' in line:
summary = line.split(':')[-1].split(':')[-1].strip()
# 如果这一行看起来像总结内容
elif len(line) > 50 and ('今日' in line or '热点' in line or '新闻' in line):
if not summary:
summary = line
# 清理关键词
clean_keywords = []
for keyword in keywords:
keyword = keyword.strip()
if keyword and len(keyword) > 1 and keyword not in clean_keywords:
clean_keywords.append(keyword)
# 如果没有找到总结,生成一个简单的
if not summary:
summary = "今日热点新闻内容丰富,涵盖了社会各个层面的关注点。"
return clean_keywords[:max_keywords], summary
def _extract_simple_keywords(self, news_list: List[Dict]) -> List[str]:
"""简单关键词提取(fallback方案)"""
keywords = []
for news in news_list:
title = news.get('title', '')
# 简单的关键词提取
# 移除常见的无意义词汇
title_clean = re.sub(r'[#@【】\[\]()()]', ' ', title)
words = title_clean.split()
for word in words:
word = word.strip()
if (len(word) > 1 and
word not in ['的', '了', '在', '和', '与', '或', '但', '是', '有', '被', '将', '已', '正在'] and
word not in keywords):
keywords.append(word)
return keywords[:10]
def get_search_keywords(self, keywords: List[str], limit: int = 10) -> List[str]:
"""
获取用于搜索的关键词
Args:
keywords: 关键词列表
limit: 限制数量
Returns:
适合搜索的关键词列表
"""
# 过滤和优化关键词
search_keywords = []
for keyword in keywords:
keyword = str(keyword).strip()
# 过滤条件
if (len(keyword) > 1 and
len(keyword) < 20 and # 不能太长
keyword not in search_keywords and
not keyword.isdigit() and # 不是纯数字
not re.match(r'^[a-zA-Z]+$', keyword)): # 不是纯英文(除非是专有名词)
search_keywords.append(keyword)
return search_keywords[:limit]
if __name__ == "__main__":
# 测试话题提取器
extractor = TopicExtractor()
# 模拟新闻数据
test_news = [
{"title": "AI技术发展迅速", "source_platform": "科技新闻"},
{"title": "股市行情分析", "source_platform": "财经新闻"},
{"title": "明星最新动态", "source_platform": "娱乐新闻"}
]
keywords, summary = extractor.extract_keywords_and_summary(test_news)
print(f"提取的关键词: {keywords}")
print(f"新闻总结: {summary}")
search_keywords = extractor.get_search_keywords(keywords)
print(f"搜索关键词: {search_keywords}")