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") API_KEY = os.getenv("API_KEY") def fetch_news(query="ecolab", sort_by="publishedAt", page_size=10, language="auto"): """ NewsAPIからニュース情報を取得する関数 """ try: # NewsAPIの正しいヘッダー形式 headers = { "X-API-Key": API_KEY, "User-Agent": "NewsApp/1.0" } # 日本語検索の場合は日本語も含める params = { "q": query, "sortBy": sort_by, "pageSize": page_size, } # 言語設定を動的に決定 if language == "all": # すべての言語で検索(言語制限なし) pass elif language == "jp": # 日本語のソースも含めるため、言語制限を外すか日本語圏のソースを指定 pass # NewsAPIの日本語サポートは限定的なので制限を外す elif language == "en": params["language"] = "en" elif language == "auto": # 日本語文字が含まれている場合は言語制限を外す if any('\u3040' <= char <= '\u309F' or '\u30A0' <= char <= '\u30FF' or '\u4E00' <= char <= '\u9FAF' for char in query): pass # 言語制限なしで検索 else: params["language"] = "en" response = requests.get(NEWS_API_URL, headers=headers, params=params) if response.status_code == 200: data = response.json() if data["status"] == "ok" and data["totalResults"] > 0: articles = data["articles"] # データを整理 news_data = [] for article in articles: news_data.append({ "タイトル": article.get("title", "N/A"), "説明": article.get("description", "N/A"), "ソース": article.get("source", {}).get("name", "N/A"), "公開日": article.get("publishedAt", "N/A"), "URL": article.get("url", "N/A") }) # DataFrameに変換 df = pd.DataFrame(news_data) return df, f"✅ {len(articles)}件のニュースを取得しました" else: return pd.DataFrame(), "❌ ニュースが見つかりませんでした" else: return pd.DataFrame(), f"❌ APIエラー: {response.status_code} - {response.text}" except Exception as e: return pd.DataFrame(), f"❌ エラーが発生しました: {str(e)}" def search_news(query: str, sort_by: str, page_size: int, language: str = "auto"): """ Main function for searching news. Args: query (str): The search keyword for news articles. sort_by (str): The sorting method for the news results. page_size (int): The number of news articles to retrieve. language (str): Language for search results. Returns: tuple: DataFrame and status message """ if not query.strip(): return pd.DataFrame(), "❌ 検索キーワードを入力してください" df, message = fetch_news(query, sort_by, page_size, language) return df, message # Gradioインターフェース with gr.Blocks(title="📰 News API アプリ", theme=gr.themes.Soft()) as demo: gr.Markdown( """ # 📰 News API ニュース検索アプリ NewsAPIを使用してリアルタイムのニュース情報を検索・取得できます。 **🔥 日本語検索対応!** **使い方:** 1. 検索キーワードを入力(日本語・英語両方対応) 2. 言語とソート方法を選択 3. 取得件数を選択 4. 「ニュース検索」ボタンをクリック """ ) with gr.Row(): with gr.Column(): # 入力コンポーネント query_input = gr.Textbox( label="🔍 検索キーワード", placeholder="例: 東京, トヨタ, AI, ecolab, 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="📄 取得件数" ) search_btn = gr.Button("🔍 ニュース検索", variant="primary") with gr.Column(): # 出力コンポーネント status_output = gr.Textbox( label="📋 ステータス", interactive=False ) # 結果表示 results_df = gr.Dataframe( label="📰 ニュース結果", wrap=True, row_count=30, interactive=False ) # イベントハンドラー search_btn.click( fn=search_news, inputs=[query_input, sort_by_dropdown, page_size_slider, language_dropdown], outputs=[results_df, status_output], show_api=True, ) # Enter キーでも検索可能 query_input.submit( fn=search_news, inputs=[query_input, sort_by_dropdown, page_size_slider, language_dropdown], outputs=[results_df, status_output], show_api=False, ) # 初期ロード時にデフォルト検索を実行 demo.load( fn=lambda: search_news("ecolab", "publishedAt", 10, "auto"), outputs=[results_df, status_output], show_api=False, ) # アプリ起動 if __name__ == "__main__": demo.launch(mcp_server=True)