import gradio as gr import pandas as pd from pytrends.request import TrendReq import plotly.express as px import plotly.graph_objects as go DEVELOPER_NAME = "黃千宥、陳奕瑄、汪于捷、李哲弘、洪寓澤" # 初始化 pytrends # hl='zh-TW' -> 繁體中文, tz=480 -> 台灣時區 (GMT+8) pytrends = TrendReq(hl='zh-TW', tz=480) PLOTLY_TEMPLATE = "plotly_dark" def analyze_google_trends(keywords_str: str, timeframe: str): """ 根據輸入的關鍵字和時間範圍,從 Google Trends 獲取並分析資料。 Args: keywords_str: 以逗號分隔的關鍵字字串。 timeframe: Gradio 選項對應的時間範圍字串。 Returns: 一個包含兩個 Plotly 圖表的元組 (時間趨勢圖, 區域熱度圖)。 """ if not keywords_str: gr.Warning("請至少輸入一個關鍵字!") # 回傳空的圖表 return go.Figure(), go.Figure() # 解析關鍵字 kw_list = [kw.strip() for kw in keywords_str.split(',')] if len(kw_list) > 5: gr.Warning("為了圖表清晰,最多支援比較 5 個關鍵字。") kw_list = kw_list[:5] # 對應 Gradio 選項到 pytrends 的時間格式 timeframe_map = { "過去 7 天": 'now 7-d', "過去一個月": 'today 1-m', "過去三個月": 'today 3-m', "過去一年": 'today 12-m', } selected_timeframe = timeframe_map.get(timeframe, 'now 7-d') try: # 1. 獲取時間序列資料 pytrends.build_payload(kw_list, cat=0, timeframe=selected_timeframe, geo='', gprop='') interest_over_time_df = pytrends.interest_over_time() if interest_over_time_df.empty: gr.Warning(f"找不到關於 '{keywords_str}' 的時間趨勢資料。") time_fig = go.Figure() else: interest_over_time_df = interest_over_time_df.drop(columns=['isPartial'], errors='ignore') time_fig = plot_interest_over_time(interest_over_time_df, f"'{', '.join(kw_list)}' 在 {timeframe} 的搜尋熱度趨勢") # 2. 獲取區域熱度資料 # 注意:區域熱度分析不支援多個關鍵字同時比較,因此我們只分析第一個關鍵字 first_keyword = kw_list[0] pytrends.build_payload([first_keyword], cat=0, timeframe=selected_timeframe, geo='', gprop='') interest_by_region_df = pytrends.interest_by_region(resolution='COUNTRY', inc_low_vol=True, inc_geo_code=False) if interest_by_region_df.empty: gr.Warning(f"找不到關於 '{first_keyword}' 的區域熱度資料。") region_fig = go.Figure() else: # 只取前 20 名 interest_by_region_df = interest_by_region_df.sort_values(by=first_keyword, ascending=False).head(20) region_fig = plot_interest_by_region(interest_by_region_df, f"'{first_keyword}' 在全球的區域熱度 Top 20") return time_fig, region_fig except Exception as e: gr.Error(f"查詢時發生錯誤: {e}") return go.Figure(), go.Figure() def plot_interest_over_time(df: pd.DataFrame, title: str): """使用 Plotly 繪製時間趨勢圖。""" fig = px.line(df, x=df.index, y=df.columns, title=title, labels={'value': '相對熱度', 'date': '日期', 'variable': '關鍵字'}) fig.update_layout( template=PLOTLY_TEMPLATE, paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0.2)', legend_title_text='' ) return fig def plot_interest_by_region(df: pd.DataFrame, title: str): """使用 Plotly 繪製區域熱度長條圖。""" fig = px.bar(df, x=df.index, y=df.columns[0], title=title, labels={'y': '相對熱度', 'index': '國家/地區'}) fig.update_layout( template=PLOTLY_TEMPLATE, paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0.2)' ) fig.update_xaxes(categoryorder='total descending') return fig with gr.Blocks( theme=gr.themes.Soft( primary_hue="blue", secondary_hue="cyan", font=["Arial", "sans-serif"] ), js=""" function refresh() { const url = new URL(window.location); if (url.searchParams.get('__theme') !== 'dark') { url.searchParams.set('__theme', 'dark'); window.location.href = url.href; } } """ ) as app: gr.Markdown(f"""

📊 關鍵字趨勢分析儀表板

輸入關鍵字,洞察全球搜尋趨勢與市場脈動

Designed by: {DEVELOPER_NAME}

""") with gr.Group(): with gr.Row(): keywords_input = gr.Textbox( label="🔍 輸入關鍵字", placeholder="例如:Bitcoin, Ethereum, Dogecoin (以逗號分隔)", scale=3 ) timeframe_input = gr.Radio( ["過去 7 天", "過去一個月", "過去三個月", "過去一年"], label="🗓️ 選擇時間範圍", value="過去 7 天", scale=2 ) analyze_button = gr.Button("🚀 開始分析", variant="primary") with gr.Tabs(): with gr.TabItem("📈 時間趨勢比較"): time_series_plot = gr.Plot() with gr.TabItem("🌍 全球區域熱度"): region_plot = gr.Plot() gr.Markdown("

註:區域熱度分析僅針對您輸入的第一個關鍵字。

") analyze_button.click( fn=analyze_google_trends, inputs=[keywords_input, timeframe_input], outputs=[time_series_plot, region_plot] ) gr.Examples( examples=[ ["Bitcoin, Ethereum", "過去三個月"], ["穩定幣, Coinbase", "過去一年"], ["NVIDIA, AMD, TSMC", "過去一個月"], ], inputs=[keywords_input, timeframe_input] ) app.launch(share=True, debug=False, show_error=True, show_api=False)