This commit is contained in:
songzhuoyuan
2026-08-11 20:41:31 +08:00
parent 2ce13e5bae
commit befdc32aea
24 changed files with 1311 additions and 530 deletions
+84 -7
View File
@@ -1,6 +1,6 @@
"""
新闻数据获取模块
使用akshare获取股票的最新新闻信息(替代qstock
优先使用tushare,失败时使用akshare获取股票的最新新闻信息
"""
import pandas as pd
@@ -9,6 +9,7 @@ import io
import warnings
from datetime import datetime, timedelta
import akshare as ak
from data_source_manager import data_source_manager
warnings.filterwarnings('ignore')
@@ -30,6 +31,9 @@ def _setup_stdout_encoding():
_setup_stdout_encoding()
# 记录tushare news接口是否无权限(避免每次分析都重复请求失败)
_tushare_news_unavailable = False
class QStockNewsDataFetcher:
"""新闻数据获取类(使用akshare作为数据源)"""
@@ -37,7 +41,7 @@ class QStockNewsDataFetcher:
def __init__(self):
self.max_items = 30 # 最多获取的新闻数量
self.available = True
print("✓ 新闻数据获取器初始化成功(akshare数据源")
print("✓ 新闻数据获取器初始化成功(tushare优先/akshare备用")
def get_stock_news(self, symbol):
"""
@@ -67,7 +71,7 @@ class QStockNewsDataFetcher:
try:
# 获取新闻数据
print(f"📰 正在使用qstock获取 {symbol} 的最新新闻...")
print(f"📰 正在获取 {symbol} 的最新新闻...")
news_data = self._get_news_data(symbol)
if news_data:
@@ -89,9 +93,20 @@ class QStockNewsDataFetcher:
return symbol.isdigit() and len(symbol) == 6
def _get_news_data(self, symbol):
"""获取新闻数据(使用akshare"""
"""获取新闻数据(优先tushare,失败时使用akshare"""
try:
print(f" 使用 akshare 获取新闻...")
# 优先使用tushare新闻接口
tushare_items = self._get_news_from_tushare(symbol)
if tushare_items:
print(f" ✓ 从tushare获取到 {len(tushare_items)} 条相关新闻")
return {
"items": tushare_items,
"count": len(tushare_items),
"query_time": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
"date_range": "最近新闻"
}
print(f" 使用 akshare 获取新闻(备用数据源)...")
news_items = []
@@ -223,6 +238,69 @@ class QStockNewsDataFetcher:
traceback.print_exc()
return None
def _get_news_from_tushare(self, symbol):
"""从tushare获取个股新闻(按股票名称/代码过滤)"""
global _tushare_news_unavailable
try:
if _tushare_news_unavailable or not data_source_manager.tushare_available:
return None
# 获取股票名称
stock_name = None
try:
basic = data_source_manager.get_stock_basic_info(symbol)
if basic and basic.get('name') and basic['name'] != '未知':
stock_name = basic['name']
except Exception as e:
print(f" 获取股票名称失败: {e}")
# 查询最近7天的全市场新闻(东方财富源)
end_date = datetime.now().strftime('%Y-%m-%d')
start_date = (datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d')
df = data_source_manager.tushare_api.news(
src='eastmoney',
start_date=start_date,
end_date=end_date
)
if df is None or df.empty:
return None
# 按股票代码或名称过滤
mask = df['title'].str.contains(symbol, na=False) | df['title'].str.contains(stock_name, na=False) if stock_name else df['title'].str.contains(symbol, na=False)
if 'content' in df.columns:
mask = mask | df['content'].str.contains(symbol, na=False)
if stock_name:
mask = mask | df['content'].str.contains(stock_name, na=False)
df_filtered = df[mask]
if df_filtered.empty:
return None
news_items = []
for _, row in df_filtered.head(self.max_items).iterrows():
item = {'source': 'tushare-东方财富'}
for col in ['title', 'content', 'pub_time']:
if col in df_filtered.columns:
value = row.get(col)
if value is None or (isinstance(value, float) and pd.isna(value)):
continue
try:
item[col] = str(value)
except:
item[col] = "无法解析"
if len(item) > 1:
news_items.append(item)
return news_items or None
except Exception as e:
error_msg = str(e)
if "权限" in error_msg or "积分" in error_msg:
_tushare_news_unavailable = True
print(" ⚠ tushare news 接口需要较高积分,当前账号无权限,已自动使用 akshare 获取新闻")
else:
print(f" ⚠ 从tushare获取新闻失败: {error_msg}")
return None
def format_news_for_ai(self, data):
"""
将新闻数据格式化为适合AI阅读的文本
@@ -236,7 +314,7 @@ class QStockNewsDataFetcher:
if data.get("news_data"):
news_data = data["news_data"]
text_parts.append(f"""
【最新新闻 - akshare数据源
【最新新闻 - tushare/akshare自动切换
查询时间:{news_data.get('query_time', 'N/A')}
时间范围:{news_data.get('date_range', 'N/A')}
新闻数量:{news_data.get('count', 0)}
@@ -303,4 +381,3 @@ if __name__ == "__main__":
print(f"\n获取失败: {data.get('error', '未知错误')}")
print("\n")