| import gradio as gr |
| import pandas as pd |
| import os |
|
|
| |
| FILE_PATH = "VTuber_Ultimate_Database.csv" |
| OUTPUT_DIR = "output" |
| os.makedirs(OUTPUT_DIR, exist_ok=True) |
|
|
| |
| try: |
| df = pd.read_csv(FILE_PATH) |
| unique_ids = sorted(df['ハンドル(ID)'].dropna().unique().tolist()) |
| categories = sorted(df['カテゴリ'].dropna().unique().tolist()) |
| status_msg = f"正常: {len(df)}件のレコードをデータベースから読み込みました。" |
| except Exception as e: |
| df = pd.DataFrame() |
| unique_ids, categories = [], [] |
| status_msg = f"エラー: データベースが見つかりません。" |
|
|
| |
| def filter_and_export(handles, cats, keyword_in, keyword_ex, min_views, min_date, max_results): |
| if df.empty: |
| return None, None, "エラー: データベースが空、または未接続です。" |
| |
| filtered = df.copy() |
|
|
| if handles: |
| filtered = filtered[filtered['ハンドル(ID)'].isin(handles)] |
| if cats: |
| filtered = filtered[filtered['カテゴリ'].isin(cats)] |
| if keyword_in: |
| filtered = filtered[filtered['動画タイトル'].str.contains(keyword_in, case=False, na=False)] |
| if keyword_ex: |
| filtered = filtered[~filtered['動画タイトル'].str.contains(keyword_ex, case=False, na=False)] |
| |
| filtered = filtered[filtered['総再生数'] >= min_views] |
| if min_date: |
| filtered = filtered[filtered['投稿日'] >= min_date] |
|
|
| filtered = filtered.sort_values('総再生数', ascending=False).head(max_results) |
|
|
| txt_content = f"【VTuber市場動向 解析レポート】\n" |
| txt_content += f"出力日時: {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')}\n" |
| txt_content += f"抽出レコード数: {len(filtered)}件\n" |
| txt_content += "=" * 50 + "\n\n" |
| |
| for _, row in filtered.iterrows(): |
| txt_content += f"[{row['ハンドル(ID)']}] {row['動画タイトル']}\n" |
| txt_content += f"カテゴリ: {row['カテゴリ']} | 総再生数: {row['総再生数']:,} | 高評価数: {row['高評価数']:,}\n" |
| txt_content += f"投稿日: {row['投稿日']} | URL: {row['動画URL']}\n" |
| txt_content += "-" * 50 + "\n" |
|
|
| output_path = os.path.join(OUTPUT_DIR, "Market_Analysis_Report.txt") |
| with open(output_path, "w", encoding="utf-8") as f: |
| f.write(txt_content) |
|
|
| return filtered, output_path, f"処理完了: {len(filtered)}件のデータを抽出・出力しました。" |
|
|
| custom_css = """ |
| .gradio-container input, .gradio-container textarea, .gradio-container select { |
| border: 1px solid #777 !important; |
| border-radius: 4px !important; |
| } |
| """ |
|
|
| |
| with gr.Blocks(title="VT-Analytics Pro") as app: |
| gr.Markdown("## VTuber市場解析システム (VT-Analytics Pro)") |
| gr.Markdown("検索条件を指定し、データ抽出およびレポート出力(txt形式)を実行してください。") |
| gr.Markdown(f"**システムステータス:** {status_msg}") |
|
|
| with gr.Row(): |
| with gr.Column(scale=1): |
| in_handles = gr.Dropdown(choices=unique_ids, multiselect=True, label="対象ID指定 (複数選択可・空欄で全件)") |
| in_cats = gr.CheckboxGroup(choices=categories, value=categories, label="コンテンツ種別") |
| in_kw_in = gr.Textbox(placeholder="例: 歌ってみた", label="抽出キーワード (含む)") |
| in_kw_ex = gr.Textbox(placeholder="例: 初音ミク", label="除外キーワード (含まない)") |
| |
| with gr.Row(): |
| in_min_views = gr.Number(value=100000, label="最低再生数") |
| in_min_date = gr.Textbox(value="2024-01-01", label="抽出基準日 (YYYY-MM-DD以降)") |
| |
| in_max_res = gr.Slider(minimum=10, maximum=2000, value=1000, step=10, label="最大出力件数制限 (AI解析用)") |
|
|
| search_btn = gr.Button("データ抽出実行", variant="primary") |
|
|
| with gr.Column(scale=2): |
| out_msg = gr.Textbox(label="実行ログ", interactive=False) |
| out_file = gr.File(label="解析レポート (.txt) ダウンロード", interactive=False) |
| out_df = gr.Dataframe(label="データプレビュー", interactive=False) |
|
|
| search_btn.click( |
| fn=filter_and_export, |
| inputs=[in_handles, in_cats, in_kw_in, in_kw_ex, in_min_views, in_min_date, in_max_res], |
| outputs=[out_df, out_file, out_msg] |
| ) |
|
|
| if __name__ == "__main__": |
| |
| |
| app.launch(auth=("v", "0409")) |