54justin commited on
Commit
7ba1c88
·
verified ·
1 Parent(s): f205f47

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +179 -0
app.py ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 由 Copilot 生成
2
+ """
3
+ 591租屋資料分析器 - 主程式
4
+ 高雄市鼓山區租屋市場分析工具
5
+
6
+ 此程式整合了網頁爬蟲、資料分析和視覺化功能,
7
+ 專門用於分析591租屋網的租屋資料。
8
+ """
9
+
10
+ import os
11
+ import sys
12
+ import argparse
13
+ from datetime import datetime
14
+
15
+ # 加入相對路徑
16
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
17
+
18
+ from scraper import Rent591Scraper
19
+ from analyzer import RentalDataAnalyzer
20
+ from visualizer import RentalDataVisualizer
21
+ from utils import log_message, create_output_directories, get_current_timestamp
22
+
23
+ class RentalAnalysisApp:
24
+ """591租屋分析應用程式主類別"""
25
+
26
+ def __init__(self):
27
+ self.scraper = Rent591Scraper()
28
+ self.analyzer = RentalDataAnalyzer()
29
+ self.visualizer = RentalDataVisualizer()
30
+ self.timestamp = get_current_timestamp()
31
+
32
+ def run_full_pipeline(self, max_pages: int = 5, skip_scraping: bool = False):
33
+ """執行完整的分析流程"""
34
+ print("🏠 591租屋資料分析器啟動")
35
+ print("=" * 50)
36
+
37
+ # 創建輸出目錄
38
+ create_output_directories()
39
+
40
+ # 步驟1: 資料爬取
41
+ if not skip_scraping:
42
+ log_message("開始爬取591租屋資料...")
43
+ rental_data = self.scraper.scrape_rental_data(max_pages=max_pages)
44
+
45
+ if not rental_data:
46
+ log_message("未能獲取任何資料,程式終止", "ERROR")
47
+ return False
48
+
49
+ log_message(f"成功爬取 {len(rental_data)} 筆資料")
50
+
51
+ # 儲存原始資料
52
+ self.scraper.save_data(rental_data, f"raw_data_{self.timestamp}.json")
53
+
54
+ # 轉換為CSV
55
+ df = self.scraper.to_dataframe(rental_data)
56
+ csv_filename = f"output/rental_data_{self.timestamp}.csv"
57
+ df.to_csv(csv_filename, index=False, encoding='utf-8-sig')
58
+ log_message(f"資料已儲存為CSV: {csv_filename}")
59
+
60
+ # 使用最新的資料檔案
61
+ data_file = csv_filename
62
+ else:
63
+ # 尋找最新的資料檔案
64
+ data_files = [f for f in os.listdir("output") if f.startswith("rental_data") and f.endswith(".csv")]
65
+ if not data_files:
66
+ log_message("找不到現有資料檔案,請先執行爬蟲", "ERROR")
67
+ return False
68
+ data_file = f"output/{sorted(data_files)[-1]}"
69
+ log_message(f"使用現有資料檔案: {data_file}")
70
+
71
+ # 步驟2: 資料分析
72
+ log_message("開始資料分析...")
73
+
74
+ # 載入資料
75
+ self.analyzer.load_data(data_file)
76
+
77
+ # 清洗資料
78
+ cleaned_df = self.analyzer.clean_data()
79
+ if cleaned_df is None or len(cleaned_df) == 0:
80
+ log_message("資料清洗後沒有有效資料", "ERROR")
81
+ return False
82
+
83
+ # 執行完整分析
84
+ analysis_results = self.analyzer.run_full_analysis()
85
+
86
+ # 儲存分析結果
87
+ results_filename = f"analysis_results_{self.timestamp}.json"
88
+ self.analyzer.save_analysis_results(results_filename)
89
+
90
+ # 顯示分析摘要
91
+ self.analyzer.print_summary()
92
+
93
+ # 步驟3: 資料視覺化
94
+ log_message("開始生成視覺化圖表...")
95
+
96
+ # 設置視覺化器
97
+ self.visualizer.df = cleaned_df
98
+ self.visualizer.analysis_results = analysis_results
99
+
100
+ # 生成所有圖表
101
+ self.visualizer.generate_all_visualizations()
102
+
103
+ # 創建摘要報告
104
+ summary_filename = f"output/summary_report_{self.timestamp}.png"
105
+ self.visualizer.create_summary_report(summary_filename)
106
+
107
+ log_message("分析完成!", "SUCCESS")
108
+ self.print_completion_summary()
109
+
110
+ return True
111
+
112
+ def print_completion_summary(self):
113
+ """印出完成摘要"""
114
+ print("\n" + "🎉 分析完成!" + "🎉")
115
+ print("=" * 50)
116
+ print("📁 輸出檔案:")
117
+ print(f" ├── 原始資料: output/raw_data_{self.timestamp}.json")
118
+ print(f" ├── 清洗資料: output/rental_data_{self.timestamp}.csv")
119
+ print(f" ├── 分析結果: output/analysis_results_{self.timestamp}.json")
120
+ print(f" ├── 摘要報告: output/summary_report_{self.timestamp}.png")
121
+ print(" ├── 圖表檔案:")
122
+ print(" │ ├── output/price_distribution.png")
123
+ print(" │ ├── output/price_ranges.png")
124
+ print(" │ ├── output/area_analysis.png")
125
+ print(" │ ├── output/price_per_ping.png")
126
+ print(" │ └── output/keywords_analysis.png")
127
+ print(" └── 互動式儀表板: output/dashboard.html")
128
+ print("\n💡 提示: 打開 dashboard.html 可查看互動式分析��果")
129
+ print("=" * 50)
130
+
131
+ def main():
132
+ """主函數"""
133
+ parser = argparse.ArgumentParser(description='591租屋資料分析器')
134
+ parser.add_argument('--max-pages', type=int, default=5,
135
+ help='最大爬取頁數 (預設: 5)')
136
+ parser.add_argument('--skip-scraping', action='store_true',
137
+ help='跳過爬蟲,使用現有資料進行分析')
138
+ parser.add_argument('--analysis-only', action='store_true',
139
+ help='僅執行分析,不重新爬取資料')
140
+
141
+ args = parser.parse_args()
142
+
143
+ try:
144
+ app = RentalAnalysisApp()
145
+
146
+ if args.analysis_only:
147
+ # 僅分析模式
148
+ log_message("執行僅分析模式...")
149
+ success = app.run_full_pipeline(max_pages=0, skip_scraping=True)
150
+ else:
151
+ # 完整流程
152
+ success = app.run_full_pipeline(
153
+ max_pages=args.max_pages,
154
+ skip_scraping=args.skip_scraping
155
+ )
156
+
157
+ if success:
158
+ log_message("程式執行成功完成!", "SUCCESS")
159
+ return 0
160
+ else:
161
+ log_message("程式執行失敗", "ERROR")
162
+ return 1
163
+
164
+ except KeyboardInterrupt:
165
+ log_message("使用者中斷程式執行", "WARNING")
166
+ return 1
167
+ except Exception as e:
168
+ log_message(f"程式執行時發生未預期錯誤: {e}", "ERROR")
169
+ return 1
170
+
171
+ if __name__ == "__main__":
172
+ # 設置程式資訊
173
+ print("🏠 591租屋資料分析器")
174
+ print("📍 目標區域: 高雄市鼓山區")
175
+ print("🏢 物件類型: 2房、整層、電梯大樓")
176
+ print("🔧 整合 Hugging Face 生態系統")
177
+ print("-" * 50)
178
+
179
+ exit_code = main()