tushare
This commit is contained in:
+84
-7
@@ -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")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user