Szeyu's picture
Update app.py
e3ea312 verified
import streamlit as st
import requests
from bs4 import BeautifulSoup
from transformers import pipeline
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import time
# ----------- Page Layout & Custom Styling -----------
st.set_page_config(page_title="Stock News Sentiment Analysis", layout="centered")
st.markdown("""
<style>
.main { background-color: #f9fbfc; }
.stTextInput>div>div>input {
font-size: 16px;
padding: 0.5rem;
}
.stButton>button {
background-color: #4CAF50;
color: white;
font-size: 16px;
padding: 0.5rem 1rem;
border-radius: 8px;
}
.stButton>button:hover {
background-color: #45a049;
}
</style>
""", unsafe_allow_html=True)
# ----------- Model Setup -----------
model_id = "LinkLinkWu/Boss_Stock_News_Analysis"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForSequenceClassification.from_pretrained(model_id)
sentiment_pipeline = pipeline("sentiment-analysis", model=model, tokenizer=tokenizer)
# ----------- Function Definitions -----------
def fetch_news(ticker):
try:
url = f"https://finviz.com/quote.ashx?t={ticker}"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Referer': 'https://finviz.com/',
'Connection': 'keep-alive',
}
response = requests.get(url, headers=headers)
if response.status_code != 200:
st.error(f"Failed to fetch page for {ticker}: Status code {response.status_code}")
return []
soup = BeautifulSoup(response.text, 'html.parser')
title = soup.title.text if soup.title else ""
if ticker not in title:
st.error(f"Page for {ticker} not found or access denied.")
return []
news_table = soup.find(id='news-table')
if news_table is None:
st.error(f"News table not found for {ticker}. The website structure might have changed.")
return []
news = []
for row in news_table.findAll('tr')[:50]: # Fetch up to 50 articles
a_tag = row.find('a')
if a_tag:
title = a_tag.get_text()
link = a_tag['href']
news.append({'title': title, 'link': link})
return news
except Exception as e:
st.error(f"Failed to fetch news for {ticker}: {e}")
return []
def analyze_sentiment(text):
try:
result = sentiment_pipeline(text)[0]
return "Positive" if result['label'] == 'POSITIVE' else "Negative"
except Exception as e:
st.error(f"Sentiment analysis failed: {e}")
return "Unknown"
# ----------- Streamlit UI -----------
st.title("๐Ÿ“Š Stock News Sentiment Analysis")
st.markdown("""
This tool parses stock tickers and analyzes the sentiment of related news articles.
๐Ÿ’ก *Example input:* `META, NVDA, AAPL, NTES, NCTY`
**Note:** If news fetching fails, it might be due to changes in the Finviz website structure or access restrictions. Please verify the website manually or try again later.
""")
# Input field for stock tickers
tickers_input = st.text_input("Enter stock tickers separated by commas:", "META, NVDA, AAPL, NTES, NCTY")
# Parse and display cleaned tickers in real-time
if tickers_input:
tickers = [ticker.strip().upper() for ticker in tickers_input.split(",") if ticker.strip()]
cleaned_input = ", ".join(tickers)
st.markdown(f"๐Ÿ”Ž **Parsed Tickers:** `{cleaned_input}`")
else:
tickers = []
# Button to trigger sentiment analysis
if st.button("Get News and Sentiment"):
if not tickers:
st.warning("Please enter at least one stock ticker.")
else:
progress_bar = st.progress(0)
total_stocks = len(tickers)
for idx, ticker in enumerate(tickers):
st.subheader(f"Analyzing {ticker}...")
news_list = fetch_news(ticker)
if news_list:
# Analyze sentiment for all news articles (up to 50)
sentiments = []
for news in news_list:
sentiment = analyze_sentiment(news['title'])
sentiments.append(sentiment)
# Determine overall sentiment based on majority
positive_count = sentiments.count("Positive")
negative_count = sentiments.count("Negative")
overall_sentiment = "Positive" if positive_count > negative_count else "Negative"
# Display top 3 news articles with sentiment
st.write(f"**Top 3 News Articles for {ticker}**")
for i, news in enumerate(news_list[:3], 1):
sentiment = sentiments[i-1]
st.markdown(f"{i}. [{news['title']}]({news['link']}) - **{sentiment}**")
# Display overall sentiment
st.write(f"**Overall Sentiment for {ticker}: {overall_sentiment}**")
else:
st.write(f"No news available for {ticker}.")
# Update progress bar
progress_bar.progress((idx + 1) / total_stocks)
time.sleep(0.1) # Simulate processing time