KYTHY commited on
Commit
d4665fe
·
verified ·
1 Parent(s): b935242

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +155 -121
app.py CHANGED
@@ -1,44 +1,55 @@
1
  import streamlit as st
2
- import pandas as pd
3
  import requests
4
- import numpy as np
5
- import plotly.express as px
6
- import nltk
7
- import time
8
  from datetime import datetime, timedelta
 
9
  from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
10
  from textblob import TextBlob
11
- from transformers import pipeline
12
  from wordcloud import WordCloud
 
 
 
13
  from sklearn.linear_model import LinearRegression
14
- import matplotlib.pyplot as plt
15
-
16
- # =========================================
17
- # SETUP
18
- # =========================================
19
- st.set_page_config(page_title="Financial Sentiment Analyzer", layout="wide")
20
-
21
- nltk.download("stopwords", quiet=True)
22
-
23
- # โหลดโมเดล sentiment ของ BERT
 
 
24
  @st.cache_resource
25
- def load_bert_model():
26
- return pipeline("sentiment-analysis", model="nlptown/bert-base-multilingual-uncased-sentiment")
27
-
28
- bert_model = load_bert_model()
29
- vader = SentimentIntensityAnalyzer()
30
-
31
- # ใส่ API key ของคุณ
32
- API_KEY = st.secrets["NEWS_API_KEY"] # ใส่ใน .streamlit/secrets.toml
33
- # หรือถ้ารัน local:
34
- # API_KEY = "88bc396d4eab4be494a4b86ec842db47"
35
 
36
- # =========================================
37
- # FUNCTION: ดึงข่าวจาก NewsAPI.org
38
- # =========================================
39
- @st.cache_data(ttl=3600, show_spinner="Fetching financial news...")
 
 
 
 
 
 
 
 
 
 
40
  def fetch_financial_news(keyword, days=7, limit=50):
41
- """ดึงข่าวการเงินย้อนหลังจาก NewsAPI.org"""
42
  to_date = datetime.now().strftime('%Y-%m-%d')
43
  from_date = (datetime.now() - timedelta(days=days)).strftime('%Y-%m-%d')
44
 
@@ -46,116 +57,139 @@ def fetch_financial_news(keyword, days=7, limit=50):
46
  f"https://newsapi.org/v2/everything?"
47
  f"q={keyword}+finance+stock&"
48
  f"from={from_date}&to={to_date}&"
49
- f"language=en&"
50
- f"sortBy=publishedAt&"
51
- f"pageSize={limit}&"
52
- f"apiKey={API_KEY}"
53
  )
54
 
55
- response = requests.get(url)
56
- data = response.json()
57
 
58
  if data.get("status") != "ok":
59
- st.error(f"Error fetching news: {data.get('message', 'Unknown error')}")
60
  return pd.DataFrame()
61
 
62
  articles = []
63
  for a in data["articles"]:
64
- articles.append({
65
- "date": pd.to_datetime(a["publishedAt"]),
66
- "text": f"{a['title']}\n{a.get('description', '')}",
67
- "source": a["source"]["name"],
68
- "url": a["url"]
69
- })
 
70
 
71
  return pd.DataFrame(articles)
72
 
73
- # =========================================
74
- # FUNCTION: วิเคราะห์อารมณ์
75
- # =========================================
76
- def analyze_sentiment(text):
77
- """รวมผลจาก BERT, VADER, TextBlob"""
78
- try:
79
- bert_label = bert_model(text[:512])[0]["label"]
80
- vader_score = vader.polarity_scores(text)["compound"]
81
- blob_score = TextBlob(text).sentiment.polarity
82
-
83
- bert_score = (
84
- 1 if "5" in bert_label or "4" in bert_label
85
- else -1 if "1" in bert_label or "2" in bert_label
86
- else 0
87
- )
88
-
89
- final_score = np.mean([bert_score, np.sign(vader_score), np.sign(blob_score)])
90
- return final_score
91
- except Exception:
92
- return 0
93
-
94
- # =========================================
95
- # FUNCTION: สร้า��� Word Cloud
96
- # =========================================
97
- def create_wordcloud(texts):
98
- text = " ".join(texts)
99
- wc = WordCloud(width=800, height=400, background_color="white",
100
- stopwords=set(nltk.corpus.stopwords.words("english"))).generate(text)
101
- return wc
102
-
103
- # =========================================
104
- # FUNCTION: พยากรณ์แนวโน้มอารมณ์
105
- # =========================================
106
- def forecast_sentiment_trend(df):
107
- df = df.sort_values("date")
108
- df["timestamp"] = (df["date"] - df["date"].min()).dt.days
109
- model = LinearRegression()
110
- model.fit(df[["timestamp"]], df["sentiment"])
111
- future = pd.DataFrame({"timestamp": np.arange(df["timestamp"].max()+1, df["timestamp"].max()+8)})
112
- pred = model.predict(future)
113
- return pred
114
 
115
- # =========================================
116
  # MAIN APP
117
- # =========================================
118
- st.title("💹 Financial News Sentiment Analyzer (NewsAPI.org version)")
119
- st.markdown("วิเคราะห์อารมณ์ของข่าวการเงินย้อนหลังจาก **NewsAPI.org** โดยใช้ BERT + VADER + TextBlob")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
- keyword = st.text_input("🔍 ใส่ชื่อบริษัท / หุ้น / คำค้นหา", "Tesla")
122
- limit = st.slider("จำนวนข่าวที่ต้องการดึง", 10, 100, 50)
 
 
123
 
124
- if st.button("เริ่มวิเคราะห์ข่าว"):
125
- with st.spinner(f"กำลังดึงข่าวเกี่ยวกับ '{keyword}' ..."):
126
- news_df = fetch_financial_news(keyword, days=7, limit=limit)
127
 
128
- if news_df.empty:
129
- st.error(" ไม่พบข่าวในช่วง 7 วันที่ผ่านมา")
130
- st.stop()
 
 
131
 
132
- st.success(f"✅ ดึงข่าวได้ {len(news_df)} รายการจาก NewsAPI.org")
 
 
 
 
133
 
134
- # วิเคราะห์ sentiment
135
- st.info("🔎 กำลังวิเคราะห์อารมณ์ของข่าวแต่ละรายการ...")
136
- news_df["sentiment"] = news_df["text"].apply(analyze_sentiment)
137
 
138
- # แสดงผลรวม
139
- avg_sentiment = news_df["sentiment"].mean()
140
- st.metric("📊 ค่าเฉลี่ยอารมณ์โดยรวม", f"{avg_sentiment:.2f}")
141
 
142
- # กราฟแนวโน้ม
143
- fig = px.line(news_df.sort_values("date"), x="date", y="sentiment",
144
- title=f"แนวโน้มอารมณ์ของข่าว '{keyword}'",
145
- markers=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  st.plotly_chart(fig, use_container_width=True)
147
 
148
- # Word Cloud
149
- st.subheader("☁️ คำที่ถูกใช้บ่อยในข่าว")
150
- wc = create_wordcloud(news_df["text"].tolist())
151
- st.image(wc.to_array())
152
-
153
- # พยากรณ์แนวโน้ม
154
- st.subheader("📈 พยากรณ์แนวโน้มอารมณ์ใน 7 วันข้างหน้า")
155
- forecast = forecast_sentiment_trend(news_df)
156
- st.line_chart(forecast)
157
 
158
- # แสดงข่าวต้นฉบับ
159
- st.subheader("📰 ข่าวที่ใช้ในการวิเคราะห์")
160
- for _, row in news_df.iterrows():
161
- st.markdown(f"**[{row['source']}]({row['url']})** — {row['date'].strftime('%Y-%m-%d')} \n{row['text']}")
 
1
  import streamlit as st
 
2
  import requests
3
+ import pandas as pd
 
 
 
4
  from datetime import datetime, timedelta
5
+ from transformers import pipeline
6
  from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
7
  from textblob import TextBlob
8
+ import nltk
9
  from wordcloud import WordCloud
10
+ import base64
11
+ from io import BytesIO
12
+ import numpy as np
13
  from sklearn.linear_model import LinearRegression
14
+ import plotly.graph_objects as go
15
+ import os
16
+
17
+ # --------------------------
18
+ # CONFIG
19
+ # --------------------------
20
+ st.set_page_config(page_title="📰 SentimentSync NewsAI", layout="wide")
21
+ API_KEY = "88bc396d4eab4be494a4b86ec842db47"
22
+
23
+ # --------------------------
24
+ # UTILITIES
25
+ # --------------------------
26
  @st.cache_resource
27
+ def load_models():
28
+ st.info("Loading sentiment models...")
29
+ bert_model = pipeline("sentiment-analysis", model="nlptown/bert-base-multilingual-uncased-sentiment")
30
+ vader = SentimentIntensityAnalyzer()
31
+ return bert_model, vader
32
+
33
+ def analyze_text(text, bert_model, vader):
34
+ if not text.strip():
35
+ return 0
 
36
 
37
+ vader_score = vader.polarity_scores(text)["compound"]
38
+ textblob_score = TextBlob(text).sentiment.polarity
39
+ bert_result = bert_model(text[:512])[0]
40
+ label_map = {
41
+ "1 star": -1,
42
+ "2 stars": -0.5,
43
+ "3 stars": 0,
44
+ "4 stars": 0.5,
45
+ "5 stars": 1
46
+ }
47
+ bert_score = label_map.get(bert_result["label"], 0)
48
+ return np.mean([vader_score, textblob_score, bert_score])
49
+
50
+ @st.cache_data(ttl=3600)
51
  def fetch_financial_news(keyword, days=7, limit=50):
52
+ """ดึงข่าวย้อนหลังจาก NewsAPI.org"""
53
  to_date = datetime.now().strftime('%Y-%m-%d')
54
  from_date = (datetime.now() - timedelta(days=days)).strftime('%Y-%m-%d')
55
 
 
57
  f"https://newsapi.org/v2/everything?"
58
  f"q={keyword}+finance+stock&"
59
  f"from={from_date}&to={to_date}&"
60
+ f"language=en&sortBy=publishedAt&"
61
+ f"pageSize={limit}&apiKey={API_KEY}"
 
 
62
  )
63
 
64
+ r = requests.get(url)
65
+ data = r.json()
66
 
67
  if data.get("status") != "ok":
68
+ st.error(f"API Error: {data}")
69
  return pd.DataFrame()
70
 
71
  articles = []
72
  for a in data["articles"]:
73
+ if a["description"]:
74
+ articles.append({
75
+ "date": pd.to_datetime(a["publishedAt"]),
76
+ "text": f"{a['title']} {a['description']}",
77
+ "source": a["source"]["name"],
78
+ "url": a["url"]
79
+ })
80
 
81
  return pd.DataFrame(articles)
82
 
83
+ def generate_wordcloud(text):
84
+ stopwords = nltk.corpus.stopwords.words('english')
85
+ wordcloud = WordCloud(width=800, height=400, background_color="white", stopwords=stopwords).generate(text)
86
+ buf = BytesIO()
87
+ wordcloud.to_image().save(buf, format="PNG")
88
+ return base64.b64encode(buf.getvalue()).decode()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
+ # --------------------------
91
  # MAIN APP
92
+ # --------------------------
93
+ def main():
94
+ st.title("📰 SentimentSync NewsAI")
95
+ st.markdown("วิเคราะห์แนวโน้มอารมณ์ของข่าวการเงินย้อนหลัง 7 วัน พร้อมพยากรณ์ในอนาคต")
96
+
97
+ # Sidebar
98
+ with st.sidebar:
99
+ keyword = st.text_input("ค้นหาคำ (เช่น Tesla, Bitcoin, Inflation):", "")
100
+ limit = st.slider("จำนวนข่าวที่ดึง:", 10, 100, 40)
101
+ analyze_btn = st.button("วิเคราะห์เลย")
102
+
103
+ if not analyze_btn:
104
+ st.info("กรอกคำค้นแล้วกด 'วิเคราะห์เลย' เพื่อเริ่มต้น")
105
+ return
106
+
107
+ bert_model, vader = load_models()
108
+
109
+ # ดึงข่าว
110
+ st.info(f"กำลังดึงข่าวจาก NewsAPI.org สำหรับ '{keyword}' ...")
111
+ news_df = fetch_financial_news(keyword, limit=limit)
112
+ if news_df.empty:
113
+ st.warning("ไม่พบบทความข่าวในช่วง 7 วันที่ผ่านมา")
114
+ return
115
 
116
+ # วิเคราะห์ sentiment
117
+ st.info("กำลังวิเคราะห์อารมณ์ของข่าว...")
118
+ news_df["sentiment"] = news_df["text"].apply(lambda x: analyze_text(x, bert_model, vader))
119
+ news_df["date"] = pd.to_datetime(news_df["date"])
120
 
121
+ avg_sentiment = news_df["sentiment"].mean()
122
+ pos_pct = (news_df["sentiment"] > 0.1).mean() * 100
123
+ neg_pct = (news_df["sentiment"] < -0.1).mean() * 100
124
 
125
+ col1, col2, col3 = st.columns(3)
126
+ col1.metric("ค่าเฉลี่ยอารมณ์ข่าว", f"{avg_sentiment:.2f}",
127
+ "Positive" if avg_sentiment > 0 else "Negative" if avg_sentiment < 0 else "Neutral")
128
+ col2.metric("ข่าวเชิงบวก", f"{pos_pct:.1f}%")
129
+ col3.metric("ข่าวเชิงลบ", f"{neg_pct:.1f}%")
130
 
131
+ # Wordcloud
132
+ st.subheader("☁️ Word Cloud ของข่าว")
133
+ all_text = " ".join(news_df["text"].tolist())
134
+ img = generate_wordcloud(all_text)
135
+ st.image(f"data:image/png;base64,{img}", use_column_width=True)
136
 
137
+ # แนวโน้มและพยากรณ์ในกราฟเดียว
138
+ st.subheader("📈 แนวโน้มและพยากรณ์อารมณ์ของข่าว")
 
139
 
140
+ df_sorted = news_df.sort_values("date").copy()
141
+ df_sorted["timestamp"] = (df_sorted["date"] - df_sorted["date"].min()).dt.days
 
142
 
143
+ # Train model
144
+ model = LinearRegression()
145
+ model.fit(df_sorted[["timestamp"]], df_sorted["sentiment"])
146
+
147
+ # Forecast next 7 days
148
+ future_days = 7
149
+ future_timestamps = np.arange(df_sorted["timestamp"].max() + 1, df_sorted["timestamp"].max() + future_days + 1)
150
+ future_dates = [df_sorted["date"].max() + timedelta(days=i) for i in range(1, future_days + 1)]
151
+ future_preds = model.predict(future_timestamps.reshape(-1, 1))
152
+
153
+ # Plot both actual + prediction
154
+ fig = go.Figure()
155
+
156
+ # Actual data
157
+ fig.add_trace(go.Scatter(
158
+ x=df_sorted["date"], y=df_sorted["sentiment"],
159
+ mode="lines+markers", name="Actual Sentiment",
160
+ line=dict(color="blue")
161
+ ))
162
+
163
+ # Prediction line
164
+ fig.add_trace(go.Scatter(
165
+ x=future_dates, y=future_preds,
166
+ mode="lines+markers", name="Predicted Sentiment (7-day Forecast)",
167
+ line=dict(color="orange", dash="dash")
168
+ ))
169
+
170
+ # Confidence range (±0.1)
171
+ fig.add_trace(go.Scatter(
172
+ x=future_dates + future_dates[::-1],
173
+ y=list(future_preds + 0.1) + list((future_preds - 0.1)[::-1]),
174
+ fill='toself', fillcolor='rgba(255,165,0,0.2)',
175
+ line=dict(color='rgba(255,255,255,0)'),
176
+ hoverinfo="skip",
177
+ showlegend=False
178
+ ))
179
+
180
+ fig.update_layout(
181
+ title=f"แนวโน้มและพยากรณ์อารมณ์ของข่าว '{keyword}'",
182
+ xaxis_title="วันที่",
183
+ yaxis_title="ค่าอารมณ์ (Sentiment)",
184
+ hovermode="x unified",
185
+ template="plotly_white"
186
+ )
187
  st.plotly_chart(fig, use_container_width=True)
188
 
189
+ # ตารางข่าว (optional)
190
+ st.subheader("📰 รายการข่าว")
191
+ st.dataframe(news_df[["date", "source", "text", "sentiment", "url"]], use_container_width=True)
 
 
 
 
 
 
192
 
193
+ if __name__ == "__main__":
194
+ nltk.download("stopwords", quiet=True)
195
+ main()