news-API / app.py
NAKAGAWA04K's picture
Update app.py
ab26d2c verified
import gradio as gr
import requests
import pandas as pd
from dotenv import load_dotenv
import os
load_dotenv()
# --- 環境変数と定数の設定 ---
# NewsAPI設定
NEWS_API_URL = os.getenv("NEWS_API_URL", "https://newsapi.org/v2/everything")
NEWS_API_KEY = os.getenv("API_KEY")
# Alpha Vantage API設定
ALPHA_VANTAGE_API_URL = "https://www.alphavantage.co/query"
ALPHA_VANTAGE_API_KEY = os.getenv("ALPHA_VANTAGE_API_KEY") # 環境変数から読み込む
# --- ニュース検索機能 ---
def fetch_news(query="ecolab", sort_by="publishedAt", page_size=10, language="auto"):
"""
NewsAPIからニュース情報を取得する関数
"""
if not NEWS_API_KEY:
return pd.DataFrame(), "❌ NewsAPIのAPIキーが設定されていません。"
try:
headers = {
"X-API-Key": NEWS_API_KEY,
"User-Agent": "NewsApp/1.0"
}
params = {
"q": query,
"sortBy": sort_by,
"pageSize": page_size,
}
# 言語設定
if language in ["en", "jp"]:
params["language"] = language
elif language == "auto" and not any('\u3040' <= char <= '\u309F' or '\u30A0' <= char <= '\u30FF' or '\u4E00' <= char <= '\u9FAF' for char in query):
params["language"] = "en"
response = requests.get(NEWS_API_URL, headers=headers, params=params)
response.raise_for_status() # エラーがあれば例外を発生させる
data = response.json()
if data["status"] == "ok" and data["totalResults"] > 0:
articles = data["articles"]
news_data = [{
"タイトル": article.get("title"),
"説明": article.get("description"),
"ソース": article.get("source", {}).get("name"),
"公開日": article.get("publishedAt"),
"URL": article.get("url")
} for article in articles]
df = pd.DataFrame(news_data)
return df, f"✅ {len(articles)}件のニュースを取得しました"
else:
return pd.DataFrame(), "❌ ニュースが見つかりませんでした"
except requests.exceptions.RequestException as e:
return pd.DataFrame(), f"❌ APIリクエストエラー: {e}"
except Exception as e:
return pd.DataFrame(), f"❌ 不明なエラーが発生しました: {e}"
def search_news(query: str, sort_by: str, page_size: int, language: str):
if not query.strip():
return pd.DataFrame(), "❌ 検索キーワードを入力してください"
return fetch_news(query, sort_by, page_size, language)
# --- 暗号資産情報取得機能 ---
def fetch_crypto_data(function, symbol, market):
"""
Alpha Vantage APIから暗号資産の情報を取得する関数
"""
if not ALPHA_VANTAGE_API_KEY:
return pd.DataFrame(), "❌ Alpha VantageのAPIキーが設定されていません。"
if not symbol.strip() or not market.strip():
return pd.DataFrame(), "❌ 通貨シンボルと市場を両方入力してください。"
params = {
"apikey": ALPHA_VANTAGE_API_KEY,
"function": function
}
# functionに応じてパラメータを動的に設定
if function == "CURRENCY_EXCHANGE_RATE":
params["from_currency"] = symbol.strip()
params["to_currency"] = market.strip()
else:
params["symbol"] = symbol.strip()
params["market"] = market.strip()
try:
response = requests.get(ALPHA_VANTAGE_API_URL, params=params)
response.raise_for_status()
data = response.json()
# APIからのエラーメッセージをチェック
if "Error Message" in data:
return pd.DataFrame(), f"❌ APIエラー: {data['Error Message']}"
if "Information" in data:
return pd.DataFrame(), f"ℹ️ API情報: {data['Information']}"
if not data:
return pd.DataFrame(), "❌ APIから有効なデータが返されませんでした。"
# 取得したデータに応じてDataFrameに変換
if function == "CURRENCY_EXCHANGE_RATE":
key = "Realtime Currency Exchange Rate"
if key in data:
# データを整形してDataFrameに
rate_data = {
"項目": list(data[key].keys()),
"値": list(data[key].values())
}
df = pd.DataFrame(rate_data)
return df, f"✅ {symbol}/{market}のリアルタイム価格を取得しました。"
else:
return pd.DataFrame(), "❌ リアルタイム価格のデータを取得できませんでした。"
else: # 時系列データ (DAILY, WEEKLY, MONTHLY)
# "Time Series (...)" というキーを探す
ts_key = next((k for k in data.keys() if k.startswith("Time Series")), None)
if ts_key and data[ts_key]:
df = pd.DataFrame.from_dict(data[ts_key], orient='index')
df.index.name = "日付"
# カラム名を分かりやすく変更
df.columns = [col.split('. ')[1] for col in df.columns]
return df, f"✅ {symbol}/{market}{function.replace('DIGITAL_CURRENCY_', '')}データを取得しました。"
else:
return pd.DataFrame(), "❌ 時系列データを取得できませんでした。シンボルや市場が正しいか確認してください。"
except requests.exceptions.RequestException as e:
return pd.DataFrame(), f"❌ APIリクエストエラー: {e}"
except Exception as e:
return pd.DataFrame(), f"❌ 不明なエラーが発生しました: {e}"
# --- Gradioインターフェース ---
with gr.Blocks(title="📰 総合情報検索アプリ", theme=gr.themes.Soft()) as demo:
gr.Markdown("# 📰 総合情報検索アプリ")
gr.Markdown("ニュース検索と暗号資産の価格情報を取得できます。")
with gr.Tabs():
# --- ニュース検索タブ ---
with gr.TabItem("📰 ニュース検索"):
gr.Markdown(
"""
**使い方:**
1. 検索キーワードを入力(日本語・英語両方対応)
2. 言語とソート方法を選択
3. 取得件数を選択
4. 「ニュース検索」ボタンをクリック
"""
)
with gr.Row():
with gr.Column(scale=1):
news_query_input = gr.Textbox(
label="🔍 検索キーワード",
placeholder="例: 東京, トヨタ, AI, technology...",
value="Ecolab"
)
language_dropdown = gr.Dropdown(
choices=[("自動検出", "auto"), ("日本語", "jp"), ("英語", "en"), ("すべての言語", "all")],
label="🌐 言語", value="auto"
)
sort_by_dropdown = gr.Dropdown(
choices=["publishedAt", "relevancy", "popularity"],
label="📊 ソート方法", value="publishedAt"
)
page_size_slider = gr.Slider(
minimum=5, maximum=50, value=10, step=5, label="📄 取得件数"
)
news_search_btn = gr.Button("🔍 ニュース検索", variant="primary")
with gr.Column(scale=3):
news_status_output = gr.Textbox(label="📋 ステータス", interactive=False)
news_results_df = gr.DataFrame(label="📰 ニュース結果", wrap=True, interactive=False)
# --- 暗号資産検索タブ ---
with gr.TabItem("🪙 暗号資産検索"):
gr.Markdown(
"""
**使い方:**
1. 取得したいデータの種類を選択
2. 暗号資産のシンボルを入力 (例: `BTC`, `ETH`)
3. 取引市場の通貨を入力 (例: `USD`, `EUR`)
4. 「データ取得」ボタンをクリック
**注:** `JPY`などの一部市場は、時系列データ(日足・週足・月足)でエラーになる場合があります。その際は`USD`など主要な市場をお試しください。
"""
)
with gr.Row():
with gr.Column(scale=1):
crypto_function_dropdown = gr.Dropdown(
label="📊 データ種別",
choices=[
("日足データ", "DIGITAL_CURRENCY_DAILY"),
("週足データ", "DIGITAL_CURRENCY_WEEKLY"),
("月足データ", "DIGITAL_CURRENCY_MONTHLY"),
("リアルタイム価格", "CURRENCY_EXCHANGE_RATE")
],
value="DIGITAL_CURRENCY_DAILY"
)
crypto_symbol_input = gr.Textbox(
label="🪙 通貨シンボル",
placeholder="例: BTC",
value="BTC"
)
crypto_market_input = gr.Textbox(
label="💴 取引市場",
placeholder="例: USD",
value="USD"
)
crypto_search_btn = gr.Button("🔍 データ取得", variant="primary")
with gr.Column(scale=3):
crypto_status_output = gr.Textbox(label="📋 ステータス", interactive=False)
crypto_results_df = gr.DataFrame(label="📈 暗号資産データ", wrap=True, interactive=False)
# --- イベントハンドラー ---
# ニュース検索
news_search_btn.click(
fn=search_news,
inputs=[news_query_input, sort_by_dropdown, page_size_slider, language_dropdown],
outputs=[news_results_df, news_status_output]
)
news_query_input.submit(
fn=search_news,
inputs=[news_query_input, sort_by_dropdown, page_size_slider, language_dropdown],
outputs=[news_results_df, news_status_output]
)
# 暗号資産検索
crypto_search_btn.click(
fn=fetch_crypto_data,
inputs=[crypto_function_dropdown, crypto_symbol_input, crypto_market_input],
outputs=[crypto_results_df, crypto_status_output]
)
# Enterキーでの実行(Textboxのみ)
for inp in [crypto_symbol_input, crypto_market_input]:
inp.submit(
fn=fetch_crypto_data,
inputs=[crypto_function_dropdown, crypto_symbol_input, crypto_market_input],
outputs=[crypto_results_df, crypto_status_output]
)
# 初期ロード時のデータ表示
demo.load(
fn=lambda: search_news("Ecolab", "publishedAt", 10, "auto"),
outputs=[news_results_df, news_status_output]
)
# アプリ起動
if __name__ == "__main__":
demo.launch()