|
import os |
|
import time |
|
import requests |
|
import re |
|
import pandas as pd |
|
import plotly.express as px |
|
import gradio as gr |
|
from dotenv import load_dotenv |
|
from scripts.review_summarizer import analyze_reviews |
|
|
|
load_dotenv() |
|
GEMINI_API_KEY = os.getenv('GEMINI_API_KEY') |
|
|
|
if not os.path.exists("data"): |
|
os.makedirs("data") |
|
|
|
def create_sentiment_plot(df): |
|
"""Creates a pie chart visualization for sentiment distribution""" |
|
sentiment_counts = df["sentiment_label"].value_counts() |
|
fig = px.pie( |
|
values=sentiment_counts.values, |
|
names=sentiment_counts.index, |
|
title="Duygu Analizi Dağılımı", |
|
color_discrete_map={ |
|
"Pozitif": "#2ecc71", |
|
"Nötr": "#95a5a6", |
|
"Negatif": "#e74c3c", |
|
}, |
|
) |
|
return fig |
|
|
|
def create_star_plot(df): |
|
"""Creates a bar chart visualization for star rating distribution""" |
|
star_counts = df["Yıldız Sayısı"].value_counts().sort_index() |
|
fig = px.bar( |
|
x=star_counts.index, |
|
y=star_counts.values, |
|
title="Yıldız Dağılımı", |
|
labels={"x": "Yıldız Sayısı", "y": "Yorum Sayısı"}, |
|
color_discrete_sequence=["#f39c12"], |
|
) |
|
fig.update_layout( |
|
xaxis=dict( |
|
tickmode="array", |
|
ticktext=["⭐", "⭐⭐", "⭐⭐⭐", "⭐⭐⭐⭐", "⭐⭐⭐⭐⭐"], |
|
) |
|
) |
|
return fig |
|
|
|
def scrape_product_comments_v2(url): |
|
headers = { |
|
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", |
|
"accept-language": "en-US,en;q=0.9", |
|
"cache-control": "max-age=0", |
|
"upgrade-insecure-requests": "1", |
|
"user-agent": "Mozilla/5.0 (iPad; CPU OS 14_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/129.0 Mobile/15E148 Safari/605.1.15" |
|
} |
|
|
|
|
|
match = re.search(r"-p-(\d+)", url) |
|
if not match: |
|
raise ValueError("Product ID not found in URL") |
|
|
|
product_id = match.group(1) |
|
api_url = f"https://apigw.trendyol.com/discovery-web-websfxsocialreviewrating-santral/product-reviews-detailed?contentId={product_id}&page=1&order=DESC&orderBy=Score&channelId=1" |
|
|
|
def fetch_reviews(api_url, headers): |
|
all_reviews = [] |
|
response = requests.get(api_url, headers=headers) |
|
if response.status_code != 200: |
|
raise ConnectionError(f"Initial request failed: {response.status_code}") |
|
|
|
data = response.json() |
|
total_pages = data["result"]["productReviews"]["totalPages"] |
|
all_reviews.extend(data["result"]["productReviews"]["content"]) |
|
|
|
for page in range(2, total_pages + 1): |
|
paginated_url = api_url.replace("page=1", f"page={page}") |
|
response = requests.get(paginated_url, headers=headers) |
|
if response.status_code == 200: |
|
page_data = response.json() |
|
all_reviews.extend(page_data["result"]["productReviews"]["content"]) |
|
else: |
|
print(f"Failed to fetch page {page}: {response.status_code}") |
|
|
|
return all_reviews |
|
|
|
reviews = fetch_reviews(api_url, headers) |
|
reviews_df = pd.DataFrame(reviews) |
|
reviews_df = reviews_df.rename(columns={ |
|
"id": "Kullanıcı_id", |
|
"userFullName": "Kullanıcı Adı", |
|
"comment": "Yorum", |
|
"lastModifiedDate": "Tarih", |
|
"rate": "Yıldız Sayısı" |
|
}) |
|
reviews_df = reviews_df[["Kullanıcı_id", "Kullanıcı Adı", "Yorum", "Tarih", "Yıldız Sayısı"]] |
|
return reviews_df |
|
|
|
def analyze_product(url, progress=gr.Progress()): |
|
try: |
|
|
|
progress(0.1, desc="Yorumlar çekiliyor...") |
|
df = scrape_product_comments_v2(url) |
|
|
|
if df is None or len(df) == 0: |
|
return None, None, None, None, None, None, None, "Yorumlar çekilemedi. URL'yi kontrol edin." |
|
|
|
|
|
data_path = os.path.join("data", "product_comments.csv") |
|
df.to_csv(data_path, index=False, encoding="utf-8-sig") |
|
|
|
|
|
progress(0.4, desc="Yorumlar analiz ediliyor...") |
|
summary, analyzed_df = analyze_reviews(data_path, GEMINI_API_KEY) |
|
|
|
progress(0.7, desc="Sonuçlar hazırlanıyor...") |
|
|
|
|
|
total_reviews = len(df) |
|
total_analyzed = len(analyzed_df) |
|
avg_rating = f"{analyzed_df['Yıldız Sayısı'].mean():.1f}⭐" |
|
positive_ratio = len(analyzed_df[analyzed_df["sentiment_label"] == "Pozitif"]) / len(analyzed_df) * 100 |
|
positive_ratio_str = f"%{positive_ratio:.1f}" |
|
|
|
|
|
sentiment_plot = create_sentiment_plot(analyzed_df) |
|
star_plot = create_star_plot(analyzed_df) |
|
|
|
|
|
removed_reviews = total_reviews - total_analyzed |
|
info_message = "" |
|
if removed_reviews > 0: |
|
info_message = f"Not: Toplam {removed_reviews} adet kargo, teslimat ve satıcı ile ilgili yorum analiz dışı bırakılmıştır." |
|
|
|
progress(1.0, desc="Analiz tamamlandı!") |
|
|
|
return ( |
|
str(total_reviews), |
|
str(total_analyzed), |
|
avg_rating, |
|
positive_ratio_str, |
|
sentiment_plot, |
|
star_plot, |
|
summary, |
|
info_message |
|
) |
|
|
|
except Exception as e: |
|
return None, None, None, None, None, None, None, f"Bir hata oluştu: {str(e)}" |
|
|
|
|
|
with gr.Blocks(title="Trendyol Yorum Analizi") as demo: |
|
gr.Markdown(""" |
|
# Trendyol Yorum Analizi |
|
|
|
Bu uygulama, Trendyol ürün sayfasındaki yorumları çeker, analiz eder ve özetler. |
|
""") |
|
|
|
with gr.Row(): |
|
url_input = gr.Textbox( |
|
label="Trendyol Ürün Yorumları URL", |
|
placeholder="ürünün linki" |
|
) |
|
|
|
analyze_btn = gr.Button("Analiz Et") |
|
|
|
with gr.Row(): |
|
total_reviews = gr.Textbox(label="Toplam Yorum") |
|
total_analyzed = gr.Textbox(label="Ürün Değerlendirme Sayısı") |
|
avg_rating = gr.Textbox(label="Ortalama Puan") |
|
positive_ratio = gr.Textbox(label="Olumlu Yorum Oranı") |
|
|
|
|
|
|
|
summary = gr.Markdown(label="📝 Genel Değerlendirme") |
|
info_message = gr.Markdown() |
|
|
|
with gr.Row(): |
|
sentiment_plot = gr.Plot() |
|
star_plot = gr.Plot() |
|
|
|
error_message = gr.Markdown() |
|
|
|
analyze_btn.click( |
|
analyze_product, |
|
inputs=[url_input], |
|
outputs=[ |
|
total_reviews, |
|
total_analyzed, |
|
avg_rating, |
|
positive_ratio, |
|
sentiment_plot, |
|
star_plot, |
|
summary, |
|
error_message |
|
] |
|
) |
|
|
|
if __name__ == "__main__": |
|
demo.launch() |