ytbot / app.py
Brunohdez's picture
Update app.py
8125ddb verified
import streamlit as st
import pandas as pd
import matplotlib.pyplot as plt
from transformers import pipeline
from youtube_comment_downloader import YoutubeCommentDownloader
from wordcloud import WordCloud
from langdetect import detect
import re
import yt_dlp # para futuros metadatos, pero ya no se usa aquí
st.set_page_config(page_title="Análisis de Sentimientos en YouTube", layout="wide")
st.markdown("## 🤖 Análisis con IA de Comentarios de YouTube")
st.markdown("Descubre qué opina la gente sobre cualquier video al instante 🎯")
video_url = st.text_input("🔗 Ingresa el link de un video de YouTube:")
max_comments = st.slider("💬 ¿Cuántos comentarios deseas analizar?", 10, 500, 100, step=10)
def extract_video_id(url):
match = re.search(r"v=([a-zA-Z0-9_-]{11})", url)
return match.group(1) if match else None
@st.cache_data(show_spinner=False)
def get_comments(video_id, max_comments=50):
downloader = YoutubeCommentDownloader()
comments = downloader.get_comments_from_url(
f"https://www.youtube.com/watch?v={video_id}",
sort_by=0
)
texts = []
for i, comment in enumerate(comments):
if i >= max_comments:
break
texts.append(comment['text'])
return texts
@st.cache_resource
def load_model(language):
return pipeline("sentiment-analysis", model="nlptown/bert-base-multilingual-uncased-sentiment")
if video_url:
video_id = extract_video_id(video_url)
if video_id:
# ✅ Mostrar solo la miniatura y botón al video, sin el título
st.image(f"https://img.youtube.com/vi/{video_id}/0.jpg", caption="Miniatura del video", width=480)
st.markdown(f"[🔗 Ver en YouTube](https://www.youtube.com/watch?v={video_id})")
with st.spinner("🔍 Extrayendo comentarios..."):
comments = get_comments(video_id, max_comments=max_comments)
st.write(f"💬 Se extrajeron **{len(comments)}** comentarios")
if comments:
try:
idioma = detect(comments[0])
except:
idioma = "desconocido"
st.markdown(f"🌐 **Idioma detectado:** `{idioma}`")
model = load_model(idioma)
with st.spinner("🧠 Analizando sentimientos..."):
results = model(comments)
sentiments = []
for r in results:
label = r['label']
if label in ["1 star", "2 stars"]:
sentiments.append("Negativo")
elif label == "3 stars":
sentiments.append("Neutral")
else:
sentiments.append("Positivo")
df = pd.DataFrame({"Comentario": comments, "Sentimiento": sentiments})
sentiment_counts = df["Sentimiento"].value_counts()
total = sentiment_counts.sum()
dominant = sentiment_counts.idxmax()
pos = sentiment_counts.get("Positivo", 0)
neutro = sentiment_counts.get("Neutral", 0)
neg = sentiment_counts.get("Negativo", 0)
score = (pos + 0.5 * neutro) / total
st.metric("🎯 Probabilidad de éxito del video", f"{score*100:.1f} %")
if score > 0.75:
st.success("🚀 ¡Este video tiene alto potencial de viralidad!")
elif score > 0.5:
st.info("📈 Este video tiene una recepción mayormente positiva.")
else:
st.warning("😐 El video podría no estar siendo bien recibido.")
tab1, tab2, tab3 = st.tabs(["📊 Análisis", "☁️ Palabras", "📌 Comentarios clave"])
with tab1:
st.dataframe(df)
fig1, ax1 = plt.subplots()
sentiment_counts.plot(kind='pie', autopct='%1.1f%%', ax=ax1, ylabel="")
ax1.set_title("Distribución de Sentimientos")
st.pyplot(fig1)
with tab2:
all_text = " ".join(df["Comentario"])
wordcloud = WordCloud(width=800, height=400, background_color="white").generate(all_text)
fig_wc, ax_wc = plt.subplots()
ax_wc.imshow(wordcloud, interpolation='bilinear')
ax_wc.axis("off")
st.pyplot(fig_wc)
with tab3:
for tipo in ["Positivo", "Neutral", "Negativo"]:
ejemplo = df[df["Sentimiento"] == tipo].head(1)
if not ejemplo.empty:
st.markdown(f"**{tipo}**: _{ejemplo['Comentario'].values[0]}_")
csv = df.to_csv(index=False).encode('utf-8')
st.download_button(
label="📥 Descargar resultados como CSV",
data=csv,
file_name='sentimientos_youtube.csv',
mime='text/csv',
)
else:
st.warning("No se pudieron extraer comentarios.")
else:
st.error("❌ El link no es válido. Asegúrate de que sea un enlace de YouTube con el parámetro `v=`.")