Загрузка...

Python
Steam reviews Parser written completely by neural network

Thread in Your projects created by KanRak Jun 26, 2025. 290 views

The poll
Оценка
  1. Кал дырявый

    50%
    2
  2. Имбулька

    50%
    2
  1. KanRak
    KanRak Topic starter Jun 26, 2025 Banned 1 Feb 9, 2025
    Добрый день!
    Мне было нечем заняться и решил создать через чат джбт парсер отзывов стим :fap:

    Для полный работы бота введите эту команду в cmd

    ⁡pip install aiohttp pandas openpyxl


    Весь парсинг конвертируется в .xlsx ( Потому что так удобно сортировать список через Excel)
    В самом боте имеется проверка на кол-во отзывов на игру
    Какие данные входят:

    1. Гиперссылка на отзыв
    2. Текст отзыва
    3. Положительный/Отрицательный отзыв
    4. Кол-во helpful
    5. Кол-во funny
    6. Кол-во игр автора отзыва
    7. Кол-во отзывов автора
    8. Общее кол-во часов в игре
    Где найти appid от игры?

    Заходите на любую страницу игры стима и в url будет номерок, он нам и нужен
    [IMG]


    Скидываю это сюда, может кому-то понадобится такой парсер)
    Написан он через чат джбт

    Сам код:
    Python
    import asyncio
    import aiohttp
    import pandas as pd
    import tkinter as tk
    from tkinter import ttk, messagebox
    import threading

    class SteamReviewsApp:
    def __init__(self, root):
    self.root = root
    root.title("Steam Reviews Downloader")
    root.geometry("450x360")
    root.resizable(False, False)
    root.configure(bg="#1e1e2f")

    # Стили
    style = ttk.Style(root)
    style.theme_use('clam')

    style.configure("TLabel",
    background="#1e1e2f",
    foreground="#e0e0e0",
    font=("Segoe UI", 11))
    style.configure("TButton",
    background="#4a4e69",
    foreground="#e0e0e0",
    font=("Segoe UI", 11, "bold"),
    padding=8)
    style.map("TButton",
    background=[('active', '#22223b')])
    style.configure("TEntry",
    fieldbackground="#2a2a40",
    foreground="#f0f0f0",
    font=("Segoe UI", 11),
    padding=5)

    # Отступы вокруг элементов
    pad_opts = {'padx': 15, 'pady': 10}

    # Заголовок
    title_lbl = ttk.Label(root, text="Загрузчик отзывов Steam", font=("Segoe UI", 18, "bold"), foreground="#f2e9e4", background="#1e1e2f")
    title_lbl.pack(pady=(15,5))

    # Frame для ввода appid и кнопки проверки
    input_frame = ttk.Frame(root)
    input_frame.pack(pady=(10, 0), fill="x", padx=15)

    ttk.Label(input_frame, text="AppID игры:").grid(row=0, column=0, sticky="w", **pad_opts)
    self.appid_entry = ttk.Entry(input_frame, width=20)
    self.appid_entry.grid(row=0, column=1, sticky="w")

    self.check_button = ttk.Button(input_frame, text="Проверить", command=self.check_reviews_count)
    self.check_button.grid(row=0, column=2, padx=10)

    self.available_label = ttk.Label(root, text="Всего отзывов: не проверено", font=("Segoe UI", 10), foreground="#a7a9be", background="#1e1e2f")
    self.available_label.pack(anchor="w", padx=25, pady=(3, 10))

    # Кол-во отзывов
    count_frame = ttk.Frame(root)
    count_frame.pack(fill="x", padx=15)

    ttk.Label(count_frame, text="Количество отзывов:").grid(row=0, column=0, sticky="w", **pad_opts)
    self.num_entry = ttk.Entry(count_frame, width=20)
    self.num_entry.grid(row=0, column=1, sticky="w")

    # Progress bar
    self.progress_var = tk.IntVar()
    self.progressbar = ttk.Progressbar(root, maximum=100, variable=self.progress_var, length=400)
    self.progressbar.pack(pady=(20, 10))

    # Статус
    self.status_label = ttk.Label(root, text="Готов к запуску", font=("Segoe UI", 10), foreground="#a7a9be", background="#1e1e2f")
    self.status_label.pack()

    # Кнопка запуска
    self.start_button = ttk.Button(root, text="Начать загрузку", command=self.start_download)
    self.start_button.pack(pady=20, ipadx=10, ipady=5)

    self.loop = None
    self.reviews_downloaded = 0
    self.total_reviews = 0
    self.available_reviews = None

    def check_reviews_count(self):
    appid = self.appid_entry.get().strip()
    if not appid.isdigit():
    messagebox.showerror("Ошибка", "Введите корректный numeric AppID")
    return
    self.available_label.config(text="Проверка...", foreground="#f2cc8f")
    self.check_button.config(state="disabled")
    threading.Thread(target=self.async_check_reviews_count, args=(appid,), daemon=True).start()

    def async_check_reviews_count(self, appid):
    try:
    count = asyncio.run(self.fetch_total_reviews(appid))
    self.available_reviews = count
    self.available_label.config(text=f"Всего доступно отзывов: {count}", foreground="#90be6d")
    except Exception as e:
    self.available_label.config(text=f"Ошибка проверки: {e}", foreground="#f05454")
    finally:
    self.check_button.config(state="normal")

    async def fetch_total_reviews(self, appid):
    url = f"https://store.steampowered.com/appreviews/{appid}"
    params = {
    'json': 1,
    'filter': 'all',
    'language': 'all',
    'day_range': 9223372036854775807,
    'review_type': 'all',
    'purchase_type': 'all',
    'num_per_page': 1,
    'cursor': '*',
    }
    headers = {"User-Agent": "Mozilla/5.0"}
    async with aiohttp.ClientSession() as session:
    async with session.get(url, params=params, headers=headers) as resp:
    resp.raise_for_status()
    data = await resp.json()
    return data.get('query_summary', {}).get('total_reviews', 0)

    def start_download(self):
    appid = self.appid_entry.get().strip()
    num_str = self.num_entry.get().strip()
    if not appid.isdigit():
    messagebox.showerror("Ошибка", "Введите корректный numeric AppID")
    return
    if not num_str.isdigit() or int(num_str) <= 0:
    messagebox.showerror("Ошибка", "Введите положительное число отзывов")
    return
    if self.available_reviews is not None and int(num_str) > self.available_reviews:
    if not messagebox.askyesno("Подтверждение", f"Запрошено {num_str} отзывов, а доступно только {self.available_reviews}. Продолжить?"):
    return

    self.total_reviews = int(num_str)
    self.reviews_downloaded = 0
    self.progress_var.set(0)
    self.status_label.config(text="Запуск загрузки...", foreground="#f2cc8f")

    self.start_button.config(state="disabled")
    self.check_button.config(state="disabled")

    threading.Thread(target=self.run_async_download, args=(appid, self.total_reviews), daemon=True).start()

    def run_async_download(self, appid, total_reviews):
    asyncio.run(self.async_download(appid, total_reviews))

    async def async_download(self, appid, total_reviews):
    try:
    reviews = await self.fetch_reviews(appid, total_reviews)
    self.save_to_excel(reviews, appid)
    self.status_label.config(text=f"Загрузка завершена. Отзывы сохранены в {appid}_reviews.xlsx", foreground="#90be6d")
    except Exception as e:
    self.status_label.config(text=f"Ошибка: {e}", foreground="#f05454")
    finally:
    self.start_button.config(state="normal")
    self.check_button.config(state="normal")

    async def fetch_reviews(self, appid, max_reviews):
    reviews = []
    seen_ids = set()
    cursor = '*'
    headers = {"User-Agent": "Mozilla/5.0"}

    async with aiohttp.ClientSession() as session:
    while len(reviews) < max_reviews:
    url = f"https://store.steampowered.com/appreviews/{appid}"
    params = {
    'json': 1,
    'filter': 'all',
    'language': 'all',
    'day_range': 9223372036854775807,
    'review_type': 'all',
    'purchase_type': 'all',
    'num_per_page': 100,
    'cursor': cursor,
    }
    async with session.get(url, params=params, headers=headers) as resp:
    resp.raise_for_status()
    data = await resp.json()

    batch_reviews = data.get('reviews', [])
    if not batch_reviews:
    break

    for r in batch_reviews:
    rid = r.get('recommendationid')
    if rid not in seen_ids:
    seen_ids.add(rid)
    steamid = r['author']['steamid']
    recommendationid = rid
    review_url = f"https://steamcommunity.com/profiles/{steamid}/recommended/{appid}/#review_{recommendationid}"
    review_text = r.get('review', '')
    recommended = "Положительный" if r.get('voted_up', False) else "Отрицательный"
    helpful = r.get('votes_up', 0)
    funny = r.get('votes_funny', 0)
    games_owned = r['author'].get('num_games_owned', 0)
    reviews_posted = r['author'].get('num_reviews', 0)
    hours_played = r['author'].get('playtime_forever', 0) / 60
    reviews.append({
    'Автор': steamid,
    'Ссылка на отзыв': review_url,
    'Отзыв': review_text,
    'Тип отзыва': recommended,
    'Helpful': helpful,
    'Funny': funny,
    'Кол-во игр': games_owned,
    'Кол-во отзывов': reviews_posted,
    'Часов наиграно': round(hours_played, 2),
    })
    self.reviews_downloaded += 1
    self.root.after(0, self.update_progress)
    if len(reviews) >= max_reviews:
    break
    cursor = data.get('cursor')
    if not cursor:
    break
    return reviews

    def update_progress(self):
    percent = int(self.reviews_downloaded / self.total_reviews * 100)
    self.progress_var.set(percent)
    self.status_label.config(text=f"Загружено отзывов: {self.reviews_downloaded} из {self.total_reviews}", foreground="#e0e0e0")

    def save_to_excel(self, reviews, appid):
    df = pd.DataFrame(reviews)
    def make_hyperlink(row):
    return f'=HYPERLINK("{row["Ссылка на отзыв"]}", "{row["Автор"]}")'
    df['Ссылка на отзыв'] = df.apply(make_hyperlink, axis=1)
    df.drop(columns=['Автор'], inplace=True)
    filename = f"{appid}_reviews.xlsx"
    df.to_excel(filename, index=False)

    if __name__ == "__main__":
    root = tk.Tk()
    app = SteamReviewsApp(root)
    root.mainloop()
     
Loading...