| 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 |
|
|
| 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: |
| |
| 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=`.") |
|
|