|
import gradio as gr |
|
from googleapiclient.discovery import build |
|
from textblob import TextBlob |
|
import pandas as pd |
|
import plotly.express as px |
|
import re |
|
|
|
|
|
POSITIVE_WORDS = { |
|
'teşekkür', 'güzel', 'harika', 'muhteşem', 'süper', 'başarılı', 'mükemmel', |
|
'eğitici', 'faydalı', 'yararlı', 'iyi', 'sevdim', 'beğendim', 'bravo', |
|
'tebrik', 'excellent', 'great', 'awesome', 'amazing', 'thanks' |
|
} |
|
|
|
NEGATIVE_WORDS = { |
|
'kötü', 'berbat', 'rezalet', 'saçma', 'gereksiz', 'boş', 'yanlış', |
|
'hata', 'zayıf', 'olmamış', 'beğenmedim', 'sevmedim', 'waste', 'bad', |
|
'terrible', 'worst', 'poor', 'boring' |
|
} |
|
|
|
|
|
def get_video_id(url): |
|
"""YouTube URL'sinden video ID'sini çıkarır.""" |
|
if "v=" in url: |
|
return url.split("v=")[1].split("&")[0] |
|
elif "youtu.be" in url: |
|
return url.split("/")[-1] |
|
return None |
|
|
|
|
|
def clean_text(text): |
|
"""Metni temizler ve normalleştirir.""" |
|
text = re.sub(r'<.*?>', '', text) |
|
text = re.sub(r'http\S+|www.\S+', '', text) |
|
text = text.lower() |
|
return text.strip() |
|
|
|
|
|
def analyze_sentiment_improved(text): |
|
"""Geliştirilmiş duygu analizi.""" |
|
text = clean_text(text) |
|
words = text.split() |
|
|
|
|
|
positive_count = sum(1 for word in words if word.lower() in POSITIVE_WORDS) |
|
negative_count = sum(1 for word in words if word.lower() in NEGATIVE_WORDS) |
|
|
|
|
|
blob_sentiment = TextBlob(text).sentiment.polarity |
|
|
|
|
|
if positive_count > negative_count or blob_sentiment > 0.1: |
|
return "Pozitif" |
|
elif negative_count > positive_count or blob_sentiment < -0.1: |
|
return "Negatif" |
|
return "Nötr" |
|
|
|
|
|
def count_search_word(text, search_word): |
|
"""Aranan kelimenin metinde kaç kez geçtiğini sayar.""" |
|
if not search_word: |
|
return 0 |
|
text = clean_text(text) |
|
search_word = search_word.lower() |
|
return text.count(search_word) |
|
|
|
|
|
def analyze_video(api_key, video_url, search_word=""): |
|
"""Video istatistiklerini ve yorumları analiz eder.""" |
|
try: |
|
youtube = build('youtube', 'v3', developerKey=api_key) |
|
video_id = get_video_id(video_url) |
|
|
|
if not video_id: |
|
return "Geçersiz YouTube URL'si", None, None, None |
|
|
|
|
|
video_response = youtube.videos().list( |
|
part='statistics,snippet', |
|
id=video_id |
|
).execute() |
|
|
|
if not video_response['items']: |
|
return "Video bulunamadı", None, None, None |
|
|
|
statistics = video_response['items'][0]['statistics'] |
|
video_title = video_response['items'][0]['snippet']['title'] |
|
|
|
|
|
comments = [] |
|
nextPageToken = None |
|
total_search_word_count = 0 |
|
|
|
while True: |
|
comment_response = youtube.commentThreads().list( |
|
part='snippet', |
|
videoId=video_id, |
|
maxResults=100, |
|
pageToken=nextPageToken |
|
).execute() |
|
|
|
for item in comment_response['items']: |
|
comment_text = item['snippet']['topLevelComment']['snippet']['textDisplay'] |
|
author = item['snippet']['topLevelComment']['snippet']['authorDisplayName'] |
|
like_count = item['snippet']['topLevelComment']['snippet'].get('likeCount', 0) |
|
published_at = item['snippet']['topLevelComment']['snippet']['publishedAt'] |
|
|
|
search_word_count = count_search_word(comment_text, search_word) |
|
sentiment = analyze_sentiment_improved(comment_text) |
|
|
|
total_search_word_count += search_word_count |
|
|
|
if not search_word or search_word_count > 0: |
|
comments.append({ |
|
'Yazar': author, |
|
'Yorum': comment_text, |
|
'Duygu': sentiment, |
|
'Beğeni': like_count, |
|
'Tarih': published_at, |
|
f'"{search_word}" Kelime Sayısı': search_word_count if search_word else 0 |
|
}) |
|
|
|
nextPageToken = comment_response.get('nextPageToken') |
|
if not nextPageToken: |
|
break |
|
|
|
|
|
df = pd.DataFrame(comments) |
|
|
|
|
|
sentiment_stats = df['Duygu'].value_counts() |
|
|
|
|
|
fig = px.bar( |
|
x=sentiment_stats.index, |
|
y=sentiment_stats.values, |
|
title=f"Duygu Analizi Sonuçları - {video_title}", |
|
labels={'x': 'Duygu', 'y': 'Yorum Sayısı'}, |
|
color=sentiment_stats.index, |
|
color_discrete_map={ |
|
'Pozitif': 'green', |
|
'Negatif': 'red', |
|
'Nötr': 'gray' |
|
} |
|
) |
|
|
|
|
|
excel_path = "youtube_comments.xlsx" |
|
df.to_excel(excel_path, index=False) |
|
|
|
|
|
stats_text = f""" |
|
Video Başlığı: {video_title} |
|
|
|
Video İstatistikleri: |
|
- Görüntülenme: {statistics.get('viewCount', 'Bilgi yok')} |
|
- Beğeni: {statistics.get('likeCount', 'Bilgi yok')} |
|
- Yorum Sayısı: {statistics.get('commentCount', 'Bilgi yok')} |
|
|
|
Yorum Duygu Analizi: |
|
- Pozitif Yorum: {sentiment_stats.get('Pozitif', 0)} |
|
- Negatif Yorum: {sentiment_stats.get('Negatif', 0)} |
|
- Nötr Yorum: {sentiment_stats.get('Nötr', 0)} |
|
""" |
|
|
|
if search_word: |
|
stats_text += f""" |
|
Aranan Kelime "{search_word}": |
|
- Toplam Kullanım Sayısı: {total_search_word_count} |
|
- İçeren Yorum Sayısı: {len([c for c in comments if c[f'"{search_word}" Kelime Sayısı'] > 0])} |
|
""" |
|
|
|
return stats_text, df, fig, excel_path |
|
|
|
except Exception as e: |
|
return f"Hata oluştu: {str(e)}", None, None, None |
|
|
|
|
|
|
|
def gradio_interface(api_key, video_url, search_word=""): |
|
stats, df, plot, excel_path = analyze_video(api_key, video_url, search_word) |
|
return stats, df, plot, excel_path |
|
|
|
|
|
|
|
iface = gr.Interface( |
|
fn=gradio_interface, |
|
inputs=[ |
|
gr.Textbox(label="YouTube API Anahtarı (Youtube API Key)"), |
|
gr.Textbox(label="YouTube Video URL'si (Youtube Video URL)"), |
|
gr.Textbox(label="Yorum İçinde Aranacak Kelime (Word to Search in Comment)") |
|
], |
|
outputs=[ |
|
gr.Textbox(label="İstatistikler(Statistics)"), |
|
gr.Dataframe(label="Yorumlar ve Duygu Analizi(Comments and Sentiment Analysis)"), |
|
gr.Plot(label="Duygu Analizi Grafiği(Sentiment Analysis Chart)"), |
|
gr.File(label="Yorumları İndir (Excel)(Download comments Excel)") |
|
], |
|
title="YouTube Video Analiz Aracı(Youtube Video Analysis Tool)--->(Deployed by Eray Coşkun)", |
|
description="Video istatistiklerini ve yorum duygu analizini görüntüleyin(View video statistics and comment sentiment analysis)" |
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
iface.launch(share=True) |