Перейти к содержимому
Zone of Games Forum
0wn3df1x

Скрипты Оунедфикса

Рекомендованные сообщения

Уже долгое время я занимаюсь написанием пользовательских скриптов под разные цели и нужды.

Значительная часть скриптов связана с улучшением или расширением функционала разных сайтов (в основном магазинов игр).
За редким исключением скрипты предназначены для плагина TamperMonkey или для консоли браузеров на движке Chromium.

В основном моими скриптами пользуются друзья и знакомые и за пределы того круга общения они не выходили. Решил исправить эту ситуацию.
Сюда я залью имеющиеся скрипты на случай, если они вдруг кому-то понадобятся.

К каждому скрипту будет добавлено описание с объяснением, для чего он предназначен и в чём его удобство.

  1. Парсер игр с сайта GamesForFarm. || TamperMonkey
    Сайт GamesForFarm ориентирован на людей, которые держат фермы в Steam, т.е. закупают оптом дешёвые игры, чтобы выбивать на них карточки, продавать их и так далее. Соответственно, владельцы сайта ищут оптовые партии ключей на зарубежных площадках, а затем продают их в России с небольшой наценкой. Большая часть игр на этом сайте является откровенным мусором, но иногда можно дёшево урвать хорошие проекты (в основном они попадают на сайт из бандлов). Полезность скрипта в том, что он позволяет получить весь каталог игр сайта GamesForFarm в табличном виде. Также он позволяет узнать, есть ли в каталоге игры из вашего списка желаемого и пометить игры, которые у вас уже есть. 
    Спойлер
    
    // ==UserScript==
    // @name         GamesForFarm - Парсер
    // @namespace    http://tampermonkey.net/
    // @version      1.0
    // @description  Получает все игры в GamesForFarm и ищет имеющиеся и желаемые
    // @author       0wn3df1x
    // @match        https://gamesforfarm.com*
    // @grant        none
    // ==/UserScript==
    
    (function() {
        'use strict';
    
        // Добавляем стили
        const style = document.createElement('style');
        style.textContent = `
            .load-button {
                background-color: #171a21;
                color: #acb2b8;
                border: 1px solid #171a21;
                padding: 10px 20px;
                font-size: 16px;
                cursor: pointer;
                transition: background-color 0.3s, color 0.3s;
                margin-left: 10px;
                margin-top: 10px;
            }
            .load-button:hover {
                background-color: #66c0f4;
                color: #171a21;
            }
            .json-input {
                background-color: #171a21;
                color: #acb2b8;
                border: 1px solid #171a21;
                padding: 10px;
                font-size: 14px;
                width: 300px;
                height: 100px;
                margin-left: 10px;
                margin-top: 10px;
                resize: none;
            }
            .data-link {
                margin-left: 10px;
                margin-top: 10px;
                color: #66c0f4;
                text-decoration: none;
            }
            .data-link:hover {
                text-decoration: underline;
            }
        `;
        document.head.appendChild(style);
    
        // Добавляем кнопку "Загрузить таблицу" и текстовое поле под <div class="main__title">Каталог</div>
        const mainTitle = document.querySelector('.main__title');
        if (mainTitle) {
            const loadButton = document.createElement('button');
            loadButton.textContent = 'Загрузить таблицу';
            loadButton.className = 'load-button';
    
            const jsonInput = document.createElement('textarea');
            jsonInput.className = 'json-input';
            jsonInput.placeholder = 'Вставьте JSON сюда';
    
            const dataLink = document.createElement('a');
            dataLink.href = 'https://store.steampowered.com/dynamicstore/userdata/';
            dataLink.target = '_blank'; // Открывает ссылку в новой вкладке
            dataLink.textContent = 'Получить данные своей UserData в Steam';
            dataLink.className = 'data-link';
    
            mainTitle.parentNode.insertBefore(loadButton, mainTitle.nextSibling);
            mainTitle.parentNode.insertBefore(jsonInput, loadButton.nextSibling);
            mainTitle.parentNode.insertBefore(dataLink, jsonInput.nextSibling);
    
            // Обработчик нажатия на кнопку "Загрузить таблицу"
            loadButton.addEventListener('click', function() {
                let jsonData = {};
                try {
                    if (jsonInput.value.trim() !== '') {
                        jsonData = JSON.parse(jsonInput.value);
                    }
                } catch (e) {
                    alert('Неверный формат JSON');
                    return;
                }
    
                const rgWishlist = new Set(jsonData.rgWishlist || []);
                const rgOwnedApps = new Set(jsonData.rgOwnedApps || []);
    
                // Находим кнопку и нажимаем на неё
                const button = document.querySelector('.btn-reset.product__more-button.fn_product_more.ddd3');
                if (button) {
                    button.click();
                }
    
                // Ждём 5 секунд (увеличим время ожидания)
                setTimeout(() => {
                    // Создаем массив для хранения данных всех игр
                    const gamesData = [];
    
                    // Находим все элементы с классом product__item внутри элемента с id gamesCatalog
                    const productItems = document.querySelectorAll('#gamesCatalog .product__item');
    
                    productItems.forEach(item => {
                        // Извлекаем Steam ID
                        const steamLink = item.querySelector('a[href*="store.steampowered.com/app/"]');
                        let steamId = '-';
                        if (steamLink) {
                            const href = steamLink.getAttribute('href');
                            const match = href.match(/\/app\/(\d+)\//);
                            if (match && match[1]) {
                                steamId = match[1];
                            }
                        }
    
                        // Пропускаем элемент, если нет Steam ID
                        if (steamId === '-') {
                            return;
                        }
    
                        // Извлекаем цену
                        const priceElement = item.querySelector('.product__box-price');
                        let price = '-';
                        if (priceElement) {
                            price = priceElement.textContent.trim();
                            price = formatPrice(price);
                        }
    
                        // Извлекаем адрес картинки
                        const imageElement = item.querySelector('.product__box-image img');
                        let imageUrl = '-';
                        if (imageElement) {
                            imageUrl = imageElement.getAttribute('data-src');
                        }
    
                        // Извлекаем название игры
                        const titleElement = item.querySelector('.product__box-title a');
                        let gameTitle = '-';
                        if (titleElement) {
                            gameTitle = titleElement.textContent.trim();
                        }
    
                        // Извлекаем размер скидки
                        const discountElement = item.querySelector('.product__box-prop.prop—discount');
                        let discount = '-';
                        if (discountElement) {
                            discount = discountElement.textContent.trim();
                        }
    
                        // Проверяем наличие Steam ID в списках
                        const inWishlist = rgWishlist.has(parseInt(steamId)) ? '[VVV]' : '';
                        const inOwnedApps = rgOwnedApps.has(parseInt(steamId)) ? '[VVV]' : '';
    
                        // Добавляем данные в массив
                        gamesData.push({
                            steamId,
                            price,
                            imageUrl,
                            gameTitle,
                            discount,
                            inWishlist,
                            inOwnedApps
                        });
                    });
    
                    // Сортируем массив сначала по наличию в rgWishlist, затем по цене
                    gamesData.sort((a, b) => {
                        if (a.inWishlist && !b.inWishlist) return -1;
                        if (!a.inWishlist && b.inWishlist) return 1;
                        return parseFloat(b.price.replace(',', '.')) - parseFloat(a.price.replace(',', '.'));
                    });
    
                    // Создаем новое окно для вывода результатов
                    const newWindow = window.open("", "_blank", "width=600,height=400");
                    newWindow.document.write('<html><head><title>Результаты</title></head><body><table border="1" id="gamesTable"><tr><th>Steam ID</th><th>Название игры</th><th>Цена</th><th>Размер скидки</th>' + (jsonData.rgWishlist ? '<th>rgWishlist</th>' : '') + (jsonData.rgOwnedApps ? '<th>rgOwnedApps</th>' : '') + '<th>Адрес картинки</th></tr>');
    
                    gamesData.forEach(game => {
                        newWindow.document.write(`<tr><td>${game.steamId}</td><td>${game.gameTitle}</td><td>${game.price}</td><td>${game.discount}</td>${jsonData.rgWishlist ? `<td>${game.inWishlist}</td>` : ''}${jsonData.rgOwnedApps ? `<td>${game.inOwnedApps}</td>` : ''}<td>${game.imageUrl}</td></tr>`);
                    });
    
                    newWindow.document.write('</table></body></html>');
                    newWindow.document.close();
    
                    // Добавляем функциональность сортировки таблицы по клику на заголовки
                    const table = newWindow.document.getElementById('gamesTable');
                    if (table) {
                        const headers = table.getElementsByTagName('th');
                        for (let i = 0; i < headers.length; i++) {
                            headers[i].addEventListener('click', () => {
                                sortTable(table, i);
                            });
                        }
                    }
                }, 5000); // Ожидание 5 секунд
            });
        }
    
        // Функция для форматирования цены
        function formatPrice(price) {
            return price.replace('.', ',').replace('₽', '').trim();
        }
    
        // Функция для сортировки таблицы
        function sortTable(table, columnIndex) {
            const rows = Array.from(table.rows).slice(1);
            const isNumeric = columnIndex === 0 || columnIndex === 2; // Steam ID и Цена
    
            rows.sort((a, b) => {
                const aValue = a.cells[columnIndex].textContent;
                const bValue = b.cells[columnIndex].textContent;
    
                if (isNumeric) {
                    return parseFloat(aValue.replace(',', '.')) - parseFloat(bValue.replace(',', '.'));
                } else {
                    return aValue.localeCompare(bValue);
                }
            });
    
            // Определяем направление сортировки
            const header = table.rows[0].cells[columnIndex];
            const sortDirection = header.getAttribute('data-sort') || 'asc';
            if (sortDirection === 'asc') {
                rows.reverse();
                header.setAttribute('data-sort', 'desc');
            } else {
                header.setAttribute('data-sort', 'asc');
            }
    
            // Очищаем таблицу и добавляем отсортированные строки
            while (table.rows.length > 1) {
                table.deleteRow(1);
            }
    
            rows.forEach(row => table.appendChild(row));
        }
    })();
    Спойлер

    0. Если вы хотите узнать, какие игры, продающиеся на сайте, есть у вас в списке желаемого и на аккаунте, то читайте пункты 0-4, если вы хотите просто получить таблицу имеющихся на сайте игры, то можете перейти к пункту 5.

    Итак. Что касается списка имеющихся игр и списка желаемого — они содержатся в юзердате. Если вы не пользуетесь Steam в браузере и не вошли в аккаунт со своего браузера — надо войти, т.к. Userdata пользователя доступна только ему самому. После установки скрипта вы увидите на странице магазина три новых элемента:
    sS8iaNW.png

    1. Щёлкнув по первому элементу — вы окажетесь на странице со своей Userdata
    Там хранится информация о вашем списке желаемого, имеющихся играх и тех играх, которые вы решили скрыть в магазине. Также там есть информация о рекомендуемых жанрах и кураторах. Но ничего конфиденциального — если есть желание, можете проверить.
    wP5cmnD.png

    2. Нужно скопировать данные своей Userdata и вставить в текстовое окно.
    fuv8fee.png

    3. После чего щёлкнуть “Загрузить таблицу”. Через 5 секунд откроется новое окно, в котором вы увидите:
    50NfDjF.png

    В самом верху списка идут игры, которые есть у вас в списке желаемого. Упорядочены по убыванию цены. Затем идут все остальные игры аналогично по убыванию цены. Можно щёлкать по заголовкам, чтобы сортировать данные.

    4. Вы можете скопировать данные и вставить их на Google-таблицу. После чего можете использовать формулу Image, чтобы отобразить картинки, будет выглядеть так:
    GNJ4KRK.png

    5. Если вы не вставите Json и просто нажмёте кнопку “Загрузить таблицу”, то таблица всё равно откроется в новом окне, просто в ней не будет информации об играх, которые есть у вас на аккаунте и в списке желаемого. Таблицу также можно будет упорядочить, щёлкая по заголовкам и аналогично можно будет скопировать в Google-таблицы для дальнейшей работы.

  2. Подсветка раздач на сайте SteamGifts. || TamperMonkey
    Сайт SteamGifts предназначен для организации раздач ключей от разных игр в сервисе Steam. Раздачи организуют пользователи или разработчики. Генератор случайных чисел, встроенный в сайт, определяет победителей. Скрипт подсвечивает количество участвующих в раздаче. Если участников слишком много, то цвет вхождений красный. Если их умеренное количество - голубой. Если участников мало - светло-зелёный. Если участников очень мало - цвет становится ярко зелёным, а также добавляется галочка в угол.
    Спойлер
    
    // ==UserScript==
    // @name         SteamGifts - Подсветка раздач
    // @namespace    http://tampermonkey.net/
    // @version      0.1
    // @description  Подсвечивает раздачи, попадающие под критерии
    // @author       0wn3df1x
    // @match        https://www.steamgifts.com/*
    // @grant        none
    // ==/UserScript==
    
    (function() {
        'use strict';
    
        // Функция для определения цвета подсветки в зависимости от количества записей
        function highlightEntriesCount() {
            let entriesElements = document.querySelectorAll('a[href^="/giveaway/"][href*="entries"] span');
    
            entriesElements.forEach(element => {
                let count = parseInt(element.innerText.replace(/[^0-9]/g, ''), 10);
                let color = '';
                if (count >= 1000) {
                    color = 'background-color: #FFCCDD; color: #000000;'; // бордовый фон, белые цифры
                } else if (count >= 500) {
                    color = 'background-color: #BAF5FF; color: #000000;'; // синий фон, белые цифры
                } else if (count >= 100) {
                    color = 'background-color: #90ee90; color: #000000;'; // светло-зелёный фон, чёрные цифры
                } else {
                    color = 'background-color: #00ff00; color: #000000;'; // ярко-зелёный фон, чёрные цифры
                    if (!element.classList.contains('tick-added')) {
                        addTick(element);
                        element.classList.add('tick-added');
                    }
                }
                element.style.cssText = color;
            });
        }
    
        // Функция для добавления галочки к элементу
        function addTick(element) {
            let tick = document.createElement('i');
            tick.className = 'fa fa-check-circle';
            tick.style.color = '#00AEFF';
            tick.style.fontSize = '24px';
            tick.style.marginLeft = '10px'; // Отступ галочки на 10px вправо
            element.parentElement.appendChild(tick);
        }
    
        // Вызов функции при загрузке страницы и при изменении содержимого страницы (например, при прокрутке)
        window.addEventListener('load', highlightEntriesCount);
        window.addEventListener('scroll', highlightEntriesCount);
    })();
    Спойлер

    6DOaLg6.png

  3. Скрыватель игр для сайта SteamBuy. || TamperMonkey
    SteamBuy — это обычный сайт, где продаются ключи для Steam. На сайте есть каталог, который позволяет увидеть все имеющиеся игры, при этом в каталоге нет фильтра, который позволил бы отсеять игры со смешанными и негативными рейтингами. Мой скрипт это исправляет.
    Скрытый текст
    
    // ==UserScript==
    // @name         SteamBuy-Скрыватель
    // @namespace    http://tampermonkey/
    // @version      1
    // @description  Скрывает игры, у которых обзоры хуже "очень положительных"
    // @author       0wn3df1x
    // @match        https://steambuy.com/catalog/*
    // @grant        none
    // ==/UserScript==
    
    (function() {
        'use strict';
    
        // Функция для скрытия ненужных элементов
        const hideUnwantedProducts = () => {
            // Находим все элементы с классом 'product-item'
            const products = document.querySelectorAll('.product-item');
    
            // Проходим по каждому элементу
            products.forEach((product) => {
                // Находим элемент с классом 'product-item__unit-value'
                const review = product.querySelector('.product-item__unit-value');
                // Если элемент содержит слово "Смешанные" или "В основном положительные" или "В основном отрицательные" или "Нет обзоров", то скрываем элемент 'product-item'
                if (review && (review.textContent.includes('Смешанные') || review.textContent.includes('В основном положительные') || review.textContent.includes('Положительные') || review.textContent.includes('В основном отрицательные') || review.textContent.includes('НЕТ ОБЗОРОВ'))) {
                    product.style.display = 'none';
                }
            });
        };
    
        // Выполняем скрипт через 4 секунды после загрузки страницы
        setTimeout(hideUnwantedProducts, 4000);
    
        // Следим за изменениями в DOM, чтобы скрывать ненужные элементы при подгрузке новых данных
        const observer = new MutationObserver(() => {
            hideUnwantedProducts();
        });
    
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    })();

     

    Скрытый текст
    1. Заходим в каталог. Там можем выбрать нужный раздел, нужный жанр и так далее. Отсортировать игры по цене или как нам угодно.
    2. Сперва вы можете увидеть несколько игр — ничего страшного. Вам нужно проскроллить страницу вниз и подождать, пока вы не увидите, что игры начали прогружаться с надписью “загружаем больше товаров”.
    3. Если вы хотите, чтобы грузились игры ещё с какими-то рейтингами, то можете подредактировать вот этот момент скрипта:
      
      (review && (review.textContent.includes('Смешанные') || review.textContent.includes('В основном положительные') || review.textContent.includes('Положительные') || review.textContent.includes('В основном отрицательные') || review.textContent.includes('НЕТ ОБЗОРОВ')))

      Просто уберите ненужное.

    4. Вуаля:
      KbuQw0G.png
  4. Steam DB — Sales; конвертация тенге в рубли и SteamDB - Sales; Фильтр Недоступных для TamperMonkey были объединены в SteamDB - Sales; Улучшатор.
    Обновлённая версия находится здесь.
  5. SteamDB — Фильтр бандлов. || TamperMonkey
    На SteamDB есть раздел, который позволяет посмотреть актуальные бандлы (наборы с несколькими играми). Основная проблема этого списка наборов в том, что в нём, несмотря на наличие ряда полезных фильтров, отсутствуют самые базовые и необходимые фильтры, а именно по цене, количеству игр, скидкам и рейтингу. Скрипт это исправляет.
    Скрытый текст
    
    // ==UserScript==
    // @name         SteamDB Bundle Filter
    // @namespace    https://steamdb.info/
    // @version      0.1
    // @description  Добавляет фильтры для бандлов
    // @author       0wn3df1x
    // @match        https://steamdb.info/bundles/*
    // @grant        none
    // ==/UserScript==
    
    (function() {
        'use strict';
    
        // Парсинг цен
        function parsePrice(priceStr) {
            return parseFloat(priceStr.replace(/\s|₽/g, '').replace(',', '.'));
        }
    
        // Парсинг скидок
        function parseDiscount(discountStr) {
            return Math.abs(parseInt(discountStr.replace(/[%\-]/g, ''), 10));
        }
    
        // Парсинг рейтинга
        function parseRating(ratingStr) {
            return parseFloat(ratingStr.replace('%', ''));
        }
    
        // Управление
        const headerTitle = document.querySelector('h1.header-title');
        const filterContainer = document.createElement('div');
        filterContainer.style.marginBottom = '20px';
        filterContainer.style.fontSize = '12px';
        filterContainer.innerHTML = `
            <div>
                <label style="min-width: 100px;">Цена от:</label><input type="text" id="price-from" size="5">
                <label style="min-width: 100px;">Цена до:</label><input type="text" id="price-to" size="5">
            </div>
            <div>
                <label style="min-width: 100px;">Скидка от:</label><input type="text" id="discount-from" size="5">
                <label style="min-width: 100px;">Скидка до:</label><input type="text" id="discount-to" size="5">
            </div>
            <div>
                <label style="min-width: 100px;">Игр от:</label><input type="text" id="count-from" size="5">
                <label style="min-width: 100px;">Игр до:</label><input type="text" id="count-to" size="5">
            </div>
            <div>
                <label style="min-width: 100px;">Рейтинг от:</label><input type="text" id="rating-from" size="5">
                <label style="min-width: 100px;">Рейтинг до:</label><input type="text" id="rating-to" size="5">
            </div>
            <button id="filter-button">Фильтр</button>
        `;
        headerTitle.parentNode.insertBefore(filterContainer, headerTitle.nextSibling);
    
        // CSS
        const style = document.createElement('style');
        style.textContent = `
            label {
                display: inline-block;
                margin-right: 10px;
                text-align: left;
            }
            input[type="text"] {
                padding: 5px;
                border: 1px solid #ccc;
                border-radius: 3px;
                font-size: 12px;
            }
            button {
                padding: 5px 10px;
                border: none;
                border-radius: 3px;
                background-color: #007bff;
                color: white;
                cursor: pointer;
                font-size: 22px;
            }
            button:hover {
                background-color: #0056b3;
            }
            div {
                margin-bottom: 1px;
            }
        `;
        document.head.appendChild(style);
    
        // Логика
        document.getElementById('filter-button').addEventListener('click', function() {
            const priceFrom = parsePrice(document.getElementById('price-from').value) || 0;
            const priceTo = parsePrice(document.getElementById('price-to').value) || Infinity;
            const discountFrom = parseDiscount(document.getElementById('discount-from').value) || 0;
            const discountTo = parseDiscount(document.getElementById('discount-to').value) || Infinity;
            const countFrom = parseInt(document.getElementById('count-from').value, 10) || 0;
            const countTo = parseInt(document.getElementById('count-to').value, 10) || Infinity;
            const ratingFrom = parseRating(document.getElementById('rating-from').value) || 0;
            const ratingTo = parseRating(document.getElementById('rating-to').value) || 100;
    
            document.querySelectorAll('tr.bundle').forEach(bundle => {
                const price = parsePrice(bundle.querySelector('td.price').textContent);
                const discount = parseDiscount(bundle.querySelector('td.discount').textContent);
                const count = parseInt(bundle.querySelector('td.count').textContent, 10);
                const rating = parseRating(bundle.querySelector('td.rating').textContent);
    
                const show = (
                    price >= priceFrom && price <= priceTo &&
                    discount >= discountFrom && discount <= discountTo &&
                    count >= countFrom && count <= countTo &&
                    rating >= ratingFrom && rating <= ratingTo
                );
    
                bundle.style.display = show ? '' : 'none';
            });
        });
    })();

     

    Скрытый текст
    1. Заходим на страницу Bundles на SteamDB.
    2. Проставляем валюту “рубли” (или любую другую) и нужные нам фильтры, если необходимо.
    3. В выпадающем списке entries per page выбираем All (slow) и ждём, пока все предложения прогрузятся.
      d2xPGEh.png
       
    4. Вбиваем нужные критерии и жмём “Фильтр”
      5qX9hJg.png

    Вот, к примеру, я упорядочил по количеству игр в бандле.
    А затем накинул фильтр на то, чтоб цена была ниже 2000 и рейтинг был выше 90.
    nZrjJuL.png

  6. Навигация по Plati Market. || TamperMonkey
    Plati Market — это ещё один источник ключей. У продавцов на странице есть табличка, где можно переключать страницы товаров и выбирать метод упорядочивания.
    Проблема в том, что там выводится всего 10 товаров, при этом при прогрузке осуществляется переход по block_goods_s.asp. Если скопировать код перехода и вставить в браузер, то можно будет редактировать количество выводимых строк в самой ссылке. Но есть баг — кнопки перехода не следующую страницу не работают. Поэтому, даже если вы выставите отображение 25 игр на страницу через редактирование ссылки — вам придётся также каждый раз вписывать цифру следующей страницы для осуществления перехода. Это крайне неудобно. Мой скрипт это  исправляет:
    Он добавляет:
    - Кнопку перехода на предыдущую  и следующую страницу
    - Кнопку перехода на первую и последнюю страницу
    - Кнопку перехода на конкретную страницу
    - Текстовое поле для выбора сколько строк будет отображаться
    - Текстовое поле, куда можно вбить айди продавца
    - Текстовое поле, куда можно через запятую добавить слова для подсветки или скрытия строк, где эти слова содержатся.
    - Список, где можно выбрать, по чему упорядочивается таблица
    Скрытый текст
    
    // ==UserScript==
    // @name         Навигация по Plati.Market
    // @namespace    https://plati.market/
    // @version      1.8
    // @description  Добавление кнопок навигации, опций сортировки, управления строками и на Plati.Market
    // @author       0wn3df1x
    // @match        https://plati.market/asp/block_goods_s.asp*
    // @grant        none
    // ==/UserScript==
    
    (function() {
        'use strict';
    
        // Функция для получения текущего номера страницы из URL
        function getCurrentPage() {
            const urlParams = new URLSearchParams(window.location.search);
            return parseInt(urlParams.get('page')) || 1;
        }
    
        // Функция для получения текущей опции сортировки из URL
        function getCurrentSort() {
            const urlParams = new URLSearchParams(window.location.search);
            return urlParams.get('sort') || 'name';
        }
    
        // Функция для получения текущей опции количества строк из URL
        function getCurrentRows() {
            const urlParams = new URLSearchParams(window.location.search);
            return parseInt(urlParams.get('rows')) || 10;
        }
    
        // Функция для получения текущего id_s из URL
        function getCurrentIdS() {
            const urlParams = new URLSearchParams(window.location.search);
            return urlParams.get('id_s') || '';
        }
    
        // Функция для получения последнего номера страницы из пагинации
        function getLastPage() {
            const pagesNav = document.querySelector('.pages_nav');
            if (pagesNav) {
                const lastPageLink = pagesNav.lastChild;
                if (lastPageLink && lastPageLink.tagName === 'A') {
                    const href = lastPageLink.getAttribute('onclick');
                    const match = href.match(/Goods\(0,'price',\s*(\d+),\s*\d+,\s*'RUR'\)/);
                    if (match) {
                        return parseInt(match[1]);
                    }
                }
            }
            return 1; // По умолчанию 1, если не найдено
        }
    
        // Функция для создания кнопки навигации
        function createNavButton(text, page) {
            const button = document.createElement('button');
            button.textContent = text;
            button.className = 'steam-button';
            button.onclick = function() {
                const urlParams = new URLSearchParams(window.location.search);
                urlParams.set('page', page);
                window.location.search = urlParams.toString();
            };
            return button;
        }
    
        // Функция для создания поля ввода и кнопки для прямой навигации по страницам
        function createDirectPageNav() {
            const container = document.createElement('div');
            container.style.margin = '5px';
    
            const input = document.createElement('input');
            input.type = 'number';
            input.min = '1';
            input.value = getCurrentPage();
            input.className = 'steam-input';
    
            const button = document.createElement('button');
            button.textContent = 'Перейти';
            button.className = 'steam-button';
            button.onclick = function() {
                const page = input.value;
                const urlParams = new URLSearchParams(window.location.search);
                urlParams.set('page', page);
                window.location.search = urlParams.toString();
            };
    
            container.appendChild(input);
            container.appendChild(button);
            return container;
        }
    
        // Функция для создания поля ввода и кнопки для навигации по id_s
        function createIdSNav() {
            const container = document.createElement('div');
            container.style.margin = '5px';
    
            const input = document.createElement('input');
            input.type = 'text';
            input.value = getCurrentIdS();
            input.className = 'steam-input';
    
            const button = document.createElement('button');
            button.textContent = 'Перейти к другому продавцу';
            button.className = 'steam-button';
            button.onclick = function() {
                const id_s = input.value;
                const urlParams = new URLSearchParams(window.location.search);
                urlParams.set('id_s', id_s);
                window.location.search = urlParams.toString();
            };
    
            container.appendChild(input);
            container.appendChild(button);
            return container;
        }
    
        // Функция для создания выпадающего списка сортировки
        function createSortDropdown() {
            const sortOptions = {
                'name': 'А-я',
                'nameDESC': 'Я-а',
                'cntSell': 'Продажи (возр.)',
                'cntSellDESC': 'Продажи (убыв.)',
                'price': 'Цена (возр.)',
                'priceDESC': 'Цена (убыв.)'
            };
    
            const select = document.createElement('select');
            select.className = 'steam-select';
    
            for (const [value, text] of Object.entries(sortOptions)) {
                const option = document.createElement('option');
                option.value = value;
                option.textContent = text;
                if (value === getCurrentSort()) {
                    option.selected = true;
                }
                select.appendChild(option);
            }
    
            select.onchange = function() {
                const urlParams = new URLSearchParams(window.location.search);
                urlParams.set('sort', select.value);
                window.location.search = urlParams.toString();
            };
    
            return select;
        }
    
        // Функция для создания поля ввода и кнопки для управления количеством строк
        function createRowsControl() {
            const container = document.createElement('div');
            container.style.margin = '5px';
    
            const input = document.createElement('input');
            input.type = 'number';
            input.min = '1';
            input.max = '100';
            input.value = getCurrentRows();
            input.className = 'steam-input';
    
            const button = document.createElement('button');
            button.textContent = 'Обновить кол-во строк';
            button.className = 'steam-button';
            button.onclick = function() {
                const rows = parseInt(input.value);
                if (rows >= 1 && rows <= 100) {
                    const urlParams = new URLSearchParams(window.location.search);
                    urlParams.set('rows', rows);
                    window.location.search = urlParams.toString();
                } else {
                    input.style.borderColor = 'red';
                    button.disabled = true;
                    setTimeout(() => {
                        input.style.borderColor = '';
                        button.disabled = false;
                    }, 2000);
                }
            };
    
            input.oninput = function() {
                const rows = parseInt(input.value);
                if (rows >= 1 && rows <= 100) {
                    input.style.borderColor = '';
                    button.disabled = false;
                } else {
                    input.style.borderColor = 'red';
                    button.disabled = true;
                }
            };
    
            container.appendChild(input);
            container.appendChild(button);
            return container;
        }
    
        // Функция для создания текстового поля и кнопок для подсветки и скрытия элементов
        function createHighlightAndHideControls() {
            const container = document.createElement('div');
            container.style.margin = '5px';
    
            const input = document.createElement('input');
            input.type = 'text';
            input.placeholder = 'Введите слова через ;';
            input.className = 'steam-input';
    
            const highlightButton = document.createElement('button');
            highlightButton.textContent = 'Подсветить';
            highlightButton.className = 'steam-button';
            highlightButton.onclick = function() {
                const words = input.value.split(';').map(word => word.trim().toLowerCase());
                const rows = document.querySelectorAll('tr');
                rows.forEach(row => {
                    const title = row.querySelector('.product-title a')?.textContent.toLowerCase();
                    if (title && words.some(word => title.includes(word))) {
                        row.style.backgroundColor = 'lightyellow';
                    } else {
                        row.style.backgroundColor = '';
                    }
                });
            };
    
            const hideButton = document.createElement('button');
            hideButton.textContent = 'Скрыть';
            hideButton.className = 'steam-button';
            hideButton.onclick = function() {
                const words = input.value.split(';').map(word => word.trim().toLowerCase());
                const rows = document.querySelectorAll('tr');
                rows.forEach(row => {
                    const title = row.querySelector('.product-title a')?.textContent.toLowerCase();
                    if (title && words.some(word => title.includes(word))) {
                        row.style.display = 'none';
                    } else {
                        row.style.display = '';
                    }
                });
            };
    
            container.appendChild(input);
            container.appendChild(highlightButton);
            container.appendChild(hideButton);
            return container;
        }
    
        // Получение последнего номера страницы
        const lastPage = getLastPage();
    
        // Создание кнопок навигации
        const prevButton = createNavButton('<', getCurrentPage() - 1);
        const nextButton = createNavButton('>', getCurrentPage() + 1);
        const firstButton = createNavButton('<<', 1);
        const lastButton = createNavButton('>>', lastPage);
    
        // Создание прямой навигации по страницам
        const directPageNav = createDirectPageNav();
    
        // Создание навигации по id_s
        const idSNav = createIdSNav();
    
        // Создание выпадающего списка сортировки
        const sortDropdown = createSortDropdown();
    
        // Создание управления количеством строк
        const rowsControl = createRowsControl();
    
        // Создание контролов для подсветки и скрытия элементов
        const highlightAndHideControls = createHighlightAndHideControls();
    
        // Вставка кнопок, выпадающего списка и управления строками на страницу
        const navContainer = document.createElement('div');
        navContainer.style.display = 'flex';
        navContainer.style.justifyContent = 'left';
        navContainer.style.alignItems = 'left';
        navContainer.style.marginBottom = '20px';
    
        navContainer.appendChild(firstButton);
        navContainer.appendChild(prevButton);
        navContainer.appendChild(directPageNav);
        navContainer.appendChild(nextButton);
        navContainer.appendChild(lastButton);
        navContainer.appendChild(sortDropdown);
        navContainer.appendChild(rowsControl);
        navContainer.appendChild(idSNav);
        navContainer.appendChild(highlightAndHideControls);
    
        const sortWrapper = document.querySelector('.sort_wrapper');
        if (sortWrapper) {
            sortWrapper.innerHTML = ''; // Очистка существующего содержимого
            sortWrapper.appendChild(navContainer);
        }
    })();

     

    Скрытый текст
    1. Переходим на block_goods_s.asp.
    2. Вбиваем айди продавца и переходим.
      dyw5NIj.png
       
    3. После этого можем выбирать метод упорядочивания, количество строк на странице и так далее, всё интуитивно понятно.
  7. Скрипт для отслеживания покупок друзей на распродаже. || Консоль браузера
    Данный скрипт предназначен для тех, кто хочет узнать, какие игры чаще всего покупали на распродаже друзья. Это позволяет выявить популярные и выгодные товары.
    Скрытый текст
    
    (function() {
        // Функция для извлечения данных из элементов первого типа
        function extractDataFromFirstType(element) {
            const games = [];
            const details = element.querySelector('.blotter_gamepurchase_details');
            if (details) {
                const links = details.querySelectorAll('a');
                links.forEach(link => {
                    const href = link.getAttribute('href');
                    const match = href.match(/\/app\/(\d+)\/([^/]+)/);
                    if (match) {
                        games.push({ id: match[1], name: link.textContent.trim() });
                    }
                });
            }
            return games;
        }
    
        // Функция для извлечения данных из элементов второго типа
        function extractDataFromSecondType(element) {
            const games = [];
            const link = element.querySelector('a.es_highlight_checked');
            if (link) {
                const href = link.getAttribute('href');
                const match = href.match(/\/app\/(\d+)\/([^/]+)/);
                if (match) {
                    games.push({ id: match[1], name: link.textContent.trim() });
                }
            }
            return games;
        }
    
        // Собираем все элементы с классом blotter_gamepurchase
        const elements = document.querySelectorAll('.blotter_gamepurchase');
        const allGames = [];
    
        elements.forEach(element => {
            if (element.querySelector('.blotter_gamepurchase_details')) {
                allGames.push(...extractDataFromFirstType(element));
            } else if (element.querySelector('a.es_highlight_checked')) {
                allGames.push(...extractDataFromSecondType(element));
            }
        });
    
        // Создаем объект для подсчета количества упоминаний
        const gameCount = {};
        allGames.forEach(game => {
            if (!gameCount[game.id]) {
                gameCount[game.id] = { name: game.name, count: 0 };
            }
            gameCount[game.id].count++;
        });
    
        // Преобразуем объект в массив и сортируем по алфавиту
        const sortedGames = Object.keys(gameCount).map(id => ({ id, ...gameCount[id] }));
        sortedGames.sort((a, b) => a.name.localeCompare(b.name));
    
        // Сортируем по убыванию количества упоминаний
        sortedGames.sort((a, b) => b.count - a.count);
    
        // Создаем таблицу
        const table = document.createElement('table');
        const headerRow = table.insertRow();
        const idHeader = headerRow.insertCell();
        idHeader.textContent = 'ID игры';
        const nameHeader = headerRow.insertCell();
        nameHeader.textContent = 'Название игры';
        const countHeader = headerRow.insertCell();
        countHeader.textContent = 'Количество упоминаний';
    
        sortedGames.forEach(game => {
            const row = table.insertRow();
            const idCell = row.insertCell();
            idCell.textContent = game.id;
            const nameCell = row.insertCell();
            nameCell.textContent = game.name;
            const countCell = row.insertCell();
            countCell.textContent = game.count;
        });
    
        // Открываем новое окно с таблицей
        const newWindow = window.open('', '_blank');
        newWindow.document.body.appendChild(table);
    })();

     

    Скрытый текст
    1. Перейдите на страницу “Активность”.
    2. Пролистать до дня начала распродажи включительно.
    3. Откройте консоль (Ctrl+Shift+I) и вставьте код скрипта, после чего нажмите Enter.
    4. В новой вкладке откроется таблица.

     

    Скрытый текст
    ID игры Название игры Количество упоминаний
    499180 Braid, Anniversary Edition 3
    2835570 Buckshot Roulette 3
    632470 Disco Elysium - The Final Cut 3
    1103840 Frostpunk Digital Artbook 3
    1606200 Frostpunk Expansions Original Soundtrack 3
    966350 Frostpunk Original Soundtrack 3
    1146960 Frostpunk: The Last Autumn 3
    1125020 Frostpunk: The Rifts 3
    1466860 Age of Empires IV: Anniversary Edition 2
    1017900 Age of Empires: Definitive Edition 2
    924970 Back 4 Blood 2
    960090 Bloons TD 6 2
    204360 Castle Crashers® 2
    727850 ELDERBORN 2
    1369630 ENDER LILIES: Quietus of the Knights 2
    1293830 Forza Horizon 4 2
    1147010 Frostpunk: On The Edge 2
    1659040 HITMAN World of Assassination 2
    1244460 Jurassic World Evolution 2 2
    1846380 Need for Speed™ Unbound 2
    1184370 Pathfinder: Wrath of the Righteous - Enhanced Edition 2
    294100 RimWorld 2
    1707650 Swords and Sandals Immortals 2
    1730590 The Entropy Centre 2
    2134970 The Hotel 2
    2882070 Warframe: Цифровой Набор TennoCon-2024 2
    2186680 Warhammer 40,000: Rogue Trader 2
    525480 .hack//G.U. Last Recode 1
    1222690 «Dragon Age™ Инквизиция» 1
    2013120 1997 1
    733110 8-Bit Adventures 2 1
    2901520 Альтушка для скуфа 1
    1449320 Альфред Хичкок: «Головокружение» 1
    2727210 Бессмертный. Сказки Старой Руси 1
    3014920 Бессмертный. Сказки Старой Руси - Артбук 1
    2981660 Бессмертный. Сказки Старой Руси саундтрек 1
    292030 Ведьмак 3: Дикая Охота 1
    2648080 Городской предприниматель 1
    2584650 Девушка из Ада! 1
    2629330 МЕМОЛОГИЯ: ЕГЭ ПО МЕМАМ 1
    1282190 Набор "Экономия времени" для Need for Speed™ Rivals 1
    315210 Отряд самоубийц: Конец Лиги справедливости 1
    1444740 Путь Маньяка 1
    1629520 A Little to the Left 1
    752590 A Plague Tale: Innocence 1
    1182900 A Plague Tale: Requiem 1
    1454640 A Tale of Paper: Refolded 1
    1222700 A Way Out 1
    1336490 Against the Storm 1
    618950 Agatha Knife 1
    1817370 Age of Empires III: Definitive Edition - Mexico Civilization 1
    1581450 Age of Empires III: Definitive Edition - United States Civilization 1
    1237770 Age of Empires: Definitive Edition Soundtrack 1
    355960 Age of Mythology EX: Tale of the Dragon 1
    1076880 Air Marty 1
    1185360 Alan Sharp 1
    1150440 Aliens: Dark Descent 1
    620590 Ancestors Legacy 1
    799330 Ancestors Legacy - Digital Artbook 1
    799331 Ancestors Legacy - Digital Soundtrack 1
    1049810 Ancestors Legacy - Saladin's Conquest 1
    353980 Ankh - Anniversary Edition 1
    91200 Anomaly: Warzone Earth 1
    960980 Apsulov: End of Gods 1
    1888160 ARMORED CORE™ VI FIRES OF RUBICON™ 1
    1898300 ASKA 1
    812140 Assassin's Creed® Odyssey 1
    1769320 Athanasy 1
    1671720 Athenian Rhapsody 1
    2888120 Auto Clicker 1
    2163620 Azazel's Christmas Fable 1
    2379780 Balatro 1
    327450 Ballads of Reemus: When the Bed Bites 1
    107100 Bastion 1
    1238840 Battlefield™ 1 1
    1812450 Bellwright 1
    2556990 Beyond Good & Evil - 20th Anniversary Edition 1
    362890 Black Mesa 1
    373240 Black Sails - The Ghost Ship 1
    80350 Blackwell Convergence 1
    80360 Blackwell Deception 1
    236930 Blackwell Epiphany 1
    80340 Blackwell Unbound 1
    1587130 Blood West 1
    57640 Broken Sword: Director's Cut 1
    1734320 Brutal Orchestra 1
    2475400 Buried Alive: Breathless Rescue 1
    517790 Caesar™ 3 1
    400750 Call to Arms - Gates of Hell: Ostfront 1
    209750 Cannon Fodder 3 1
    1594320 Captain of Industry 1
    2230980 Caribbean Legend 1
    2628610 Caribbean Legend - Vile Little God 1
    504230 Celeste 1
    1092840 Celeste Soundtrack 1
    1229240 Chained Echoes 1
    2567870 Chained Together 1
    1469260 ChildStory 1
    1000640 Clam Man 1
    1700870 Clanfolk 1
    648410 Colony Ship: A Post-Earth Role Playing Game 1
    24790 Command & Conquer 3 Tiberium Wars™ 1
    47700 Command & Conquer 4: Эпилог 1
    2229850 Command & Conquer Red Alert™ 2 and Yuri’s Revenge™ 1
    2229840 Command & Conquer Red Alert™, Counterstrike™ and The Aftermath™ 1
    2229890 Command & Conquer Renegade™ 1
    24800 Command & Conquer: Red Alert 3 - Uprising 1
    24810 Command & Conquer™ 3: Kane’s Wrath 1
    2229830 Command & Conquer™ and The Covert Operations™ 1
    2229870 Command & Conquer™ Generals 1
    2732960 Command & Conquer™ Generals Zero Hour 1
    17480 Command & Conquer™ Red Alert™ 3 1
    2229880 Command & Conquer™ Tiberian Sun™ and Firestorm™ 1
    4720 Condemned: Criminal Origins 1
    2235020 Contra: Operation Galuga 1
    756800 Contraband Police 1
    1276800 CONVERGENCE: A League of Legends Story™ 1
    1926090 Coreborn 1
    1594880 Corsairs Legacy - Pirate Action RPG & Sea Battles 1
    1490130 CrashMetal - Cyberpunk 1
    1761780 Creepy Tale 3: Ingrid Penance 1
    2476100 Creepy Tale: Some Other Place 1
    368340 CrossCode 1
    960310 CrossCode - Ninja Skin 1
    1517030 CrossCode: A New Home 1
    1125910 Crowns and Pawns: Kingdom of Deceit 1
    1158310 Crusader Kings III 1
    1777000 Cut to the Core 1
    2442020 Dangeresque: The Roomisode Triungulate 1
    2102730 Dead by Daylight - Resident Evil: PROJECT W Chapter 1
    1899750 Dead by Daylight - Sadako Rising Chapter 1
    1324970 Dead By Daylight - Silent Hill Chapter 1
    1693980 Dead Space 1
    1083790 Deathbulge: Battle of the Bands 1
    18040 DeathSpank 1
    18050 DeathSpank: Thongs of Virtue 1
    548430 Deep Rock Galactic 1
    2181440 Delivery Man 1
    803330 Destroy All Humans! 1
    1450900 Desynced: Autonomous Colony Simulator 1
    504380 Detective Hayseed - Hollywood 1
    979310 Disjunction 1
    243950 Divinity: Dragon Commander 1
    1422420 Dodgeball Academia 1
    1238040 Dragon Age II: Ultimate Edition 1
    47810 Dragon Age: Origins - Ultimate Edition 1
    1902850 Dreams in the Witch House 1
    434050 Duke Nukem 3D: 20th Anniversary World Tour 1
    2379910 Dystopika 1
    715560 Eastshade 1
    977880 Eastward 1
    1273790 Empires and Tribes 1
    463530 Empires of the Undergrowth 1
    1203620 Enshrouded 1
    265610 Epic Battle Fantasy 4 1
    1469620 Epic Battle Fantasy Collection 1
    227300 Euro Truck Simulator 2 1
    1128920 EVERSPACE™ 2 1
    445190 Expeditions: Viking 1
    903850 Fairy Knights 1
    1699960 Falta 1
    552520 Far Cry® 5 1
    467430 Farming 6-in-1 bundle 1
    2299900 Felvidek 1
    503650 Fenimore Fillmore: The Westerner 1
    1181570 Feria d'Arles 1
    1748620 FlipWitch - Forbidden Sex Hex 1
    1367770 Flying propeller 1
    1308880 FORECLOSED 1
    2272250 Forgive Me Father 2 1
    323190 Frostpunk 1
    212680 FTL: Faster Than Light 1
    1224600 G String 1
    1847240 Game Dev Story 1
    1485340 Game Of Mafia 1
    80310 Gemini Rue 1
    2480560 Geneforge 2 - Infestation 1
    1967430 Ghost Trick: Phantom Detective 1
    2144740 Ghostrunner 2 1
    1474130 Girls on puzzle 2 1
    1559580 Gladihaters 1
    850190 Goat Simulator 3 1
    557600 Gorogoa 1
    661680 Growbot 1
    534550 Guacamelee! 2 1
    872750 Guard Duty 1
    2556810 Half Alive 1
    459220 Halo Wars: Definitive Edition 1
    324570 Halo: Spartan Strike 1
    1282410 Hard West 2 1
    1095120 Helheim Hassle 1
    414340 Hellblade: Senua's Sacrifice 1
    1608450 Hellslave 1
    375440 Hero-U: Rogue to Redemption 1
    1817230 Hi-Fi RUSH 1
    1434950 HighFleet 1
    2429100 Homestar Runner: Halloween Hide n' Seek 1
    477160 Human Fall Flat 1
    1351310 Hungry Boy 1
    397740 Hylics 1
    1286710 Hylics 2 1
    595140 Immortal Redneck 1
    1182620 Impostor Factory 1
    421170 Indivisible 1
    374190 Infernax 1
    1055850 Inspector Waffles 1
    911270 Intelligence: Cats 1
    835680 Intelligence: Dinosaurs 1
    957930 Intelligence: Underwater Kingdom 1
    1046030 ISLANDERS 1
    1426210 It Takes Two 1
    12340 Jack Keane 1
    58200 Jolly Rover 1
    973810 Journey To The Savage Planet 1
    1503200 Journey to the Savage Planet - Hot Garbage 1
    1977170 Jusant 1
    1131670 Justin Wack and the Big Time Hack 1
    1307890 Kingdoms Reborn 1
    1012570 Knuckle Sandwich 1
    365160 Kona 1
    2158250 Leader Pass для Civilization VI 1
    1330330 Let's Journey 1
    1033080 Letters - a written adventure 1
    1376760 Life of Delta 1
    1111590 Linked Mask 1
    1558930 Little Orpheus 1
    1667270 Lone McLonegan : A Western Adventure 1
    1328840 Lost in Play 1
    1532710 Lucy Dreaming 1
    1919100 Lulu's Temple 1
    1670870 MADiSON 1
    1949860 MADiSON - Possessed Camera DLC 1
    1030840 Mafia: Definitive Edition 1
    270610 Mage's Initiation: Reign of the Elements 1
    652950 Maggie's Apartment 1
    1184810 Mainframe Defenders 1
    588730 Majotori 1
    1708180 Man's body: For adults 1
    1363080 Manor Lords 1
    1586700 MARSUPILAMI - HOOBADVENTURE 1
    1238000 Mass Effect™: Andromeda, издание Deluxe 1
    220860 McPixel 1
    312510 Mega Coin Squad 1
    430410 Memoranda 1
    408280 Message Quest 1
    586880 Mini Ghost 1
    1388670 Modern Puzzle 1
    2696970 Monologue: Winter melancholy 1
    2817460 MORDHAU - Adventurer Pack 1
    891810 MudRunner - American Wilds Expansion 1
    2963880 Murky Divers 1
    760060 Mutant Year Zero: Road to Eden 1
    887780 Mutant Year Zero: Road to Eden - Fan Edition Content 1
    1088000 Mutant Year Zero: Seed of Evil 1
    1255560 Myst 1
    1328660 Need for Speed™ Hot Pursuit Remastered 1
    1262600 Need for Speed™ Rivals 1
    2198510 New Cycle 1
    1983990 Nexus 5X 1
    1147550 Not For Broadcast 1
    224480 Octodad: Dadliest Catch 1
    1156380 One Dreamer 1
    1335790 Operation: Tango 1
    1794910 Ostalgie: Пути Истории 1
    1886380 Ostalgie: Раздор в Югославии 1
    937170 Otaku's Adventure 1
    1013140 Outcast - A New Beginning 1
    12810 Overlord II 1
    1458140 Pacific Drive 1
    1623730 Palworld 1
    1694801 Pathfinder: Wrath of the Righteous - Season Pass 1
    2145870 Pathfinder: Wrath of the Righteous — Season Pass 2 1
    1113000 Persona 4 Golden 1
    1687950 Persona 5 Royal 1
    850320 PHOGS! 1
    2155180 Pioneers of Pagonia 1
    1608230 Planet of Lana 1
    1899060 Pocket Mirror ~ GoldenerTraum 1
    707030 POSTAL 4: No Regerts 1
    2007440 POSTAL Brain Damaged - Official Soundtrack 1
    1359980 POSTAL: Brain Damaged 1
    2015160 POSTAL: Brain Damaged Art Book 1
    1210320 Potion Craft: Alchemist Simulator 1
    1286280 Pronty 1
    982750 Puzzles for smart: Underwater Kingdom 1
    853500 Quarantine Circular 1
    2311990 Rack and Slay 1
    1355090 RAILGRADE 1
    1119730 Ranch Simulator — строительство, фермерство, охота 1
    2109308 Resident Evil 4 — особое оружие Sentinel Nine 1
    1712350 Riven 1
    1456760 ROBOBEAT 1
    1613470 Roboplant 1
    612720 SAELIG 1
    872670 SCP: 5K 1
    1690210 Sea of Dreams 1
    2907720 SEARCH ALL - ALIENS 1
    2880070 SEARCH ALL - BALLS 1
    2892220 SEARCH ALL - CANDLES 1
    2846740 SEARCH ALL - DOGS 1
    2749720 SEARCH ALL - FLIES 1
    2908900 SEARCH ALL - HEARTS 1
    2836910 SEARCH ALL - LOLLIPOPS 1
    2718070 SEARCH ALL - SNAILS 1
    2840230 SEARCH ALL - SNAKES 1
    2838500 SEARCH ALL - TOOLS 1
    2954300 SEARCH ALL - VEGGIES 1
    1509510 Settlement Survival 1
    457210 SEUM: Speedrunners from Hell 1
    713560 SEUM: The Drunk Side of the Moon 1
    1939160 Shadows Over Loathing 1
    336130 Shardlight 1
    658590 She and the Light Bearer 1
    1155330 Showgunners 1
    289070 Sid Meier’s Civilization® VI 1
    512034 Sid Meier's Civilization® VI: Australia Civilization & Scenario Pack 1
    1388850 Sid Meier's Civilization® VI: Babylon Pack 1
    1284470 Sid Meier's Civilization® VI: Byzantium & Gaul Pack 1
    1342010 Sid Meier's Civilization® VI: Catherine de Medici Persona Pack 1
    1270540 Sid Meier's Civilization® VI: Ethiopia Pack 1
    947510 Sid Meier's Civilization® VI: Gathering Storm 1
    645401 Sid Meier's Civilization® VI: Khmer and Indonesia Civilization & Scenario Pack 1
    1253990 Sid Meier's Civilization® VI: Maya & Gran Colombia Pack 1
    1308090 Sid Meier's Civilization® VI: New Frontier Pass 1
    645400 Sid Meier's Civilization® VI: Nubia Civilization & Scenario Pack 1
    512035 Sid Meier's Civilization® VI: Persia and Macedon Civilization & Scenario Pack 1
    512033 Sid Meier's Civilization® VI: Poland Civilization & Scenario Pack 1
    1478300 Sid Meier's Civilization® VI: Portugal Pack 1
    645402 Sid Meier’s Civilization® VI: Rise and Fall 1
    1281820 Sid Meier's Civilization® VI: Teddy Roosevelt Persona Pack 1
    1436950 Sid Meier's Civilization® VI: Vietnam & Kublai Khan Pack 1
    512032 Sid Meier's Civilization® VI: Vikings Scenario Pack 1
    1300 SiN Episodes: Emergence 1
    2999780 Skeletos Sword 1
    2497560 SKIBIDI: ESCAPE FROM TOILETS! 1
    214730 Space Rangers HD: A War Apart 1
    275670 Space Run 1
    355800 Space Run Galaxy 1
    943140 Sparklite 1
    1549690 SPRAWL 1
    413150 Stardew Valley 1
    251830 Stick it to The Man! 1
    1622910 Still Wakes the Deep 1
    1316760 Suicide Guy VR 1
    2423470 Suicide Guy: The Lost Dreams 1
    2080690 Sunkenland 1
    304650 SUNLESS SEA 1
    452530 Sunless Sea - Zubmariner 1
    673750 Super Bunny Man 1
    1907440 Super Lone Survivor 1
    819760 Super Seducer - Bonus Video 1: Realizing Your Value 1
    819810 Super Seducer - Bonus Video 2: Daytime Strategy 1
    819820 Super Seducer - Bonus Video 3: Earning the Kiss 1
    819830 Super Seducer - Bonus Video 4: Bridging the Gap 1
    819840 Super Seducer - Bonus Video 5: Nighttime Strategy 1
    819850 Super Seducer - The Natural (Audiobook) 1
    695920 Super Seducer : How to Talk to Girls 1
    611180 Super Street: The Game 1
    485980 Syrian Warfare 1
    938490 Syrian Warfare: Battlefields 1
    635030 Syrian Warfare: Return to Palmyra 1
    416250 Tales [PC] 1
    1457320 Techtonica 1
    1361510 Teenage Mutant Ninja Turtles: Shredder's Revenge 1
    2348930 Teenage Mutant Ninja Turtles: Shredder's Revenge - Dimension Shellshock 1
    1687210 Tell Some Story: Foz 1
    1698220 Teslagrad 2 1
    80330 The Blackwell Legacy 1
    1544020 The Callisto Protocol™ 1
    1941410 The Cub 1
    970830 The Dungeon Of Naheulbeuk: The Amulet Of Chaos 1
    22320 The Elder Scrolls III: Morrowind® Game of the Year Edition 1
    1646930 The Feeble Files 1
    311260 The Guild 3 1
    2022710 The Guild 3 Soundtrack 1
    990630 The Last Campfire 1
    284390 The Last Door - Collector's Edition 1
    402530 The Last Door: Season 2 - Collector's Edition 1
    251150 The Legend of Heroes: Trails in the Sky 1
    1457080 The Mageseeker: A League of Legends Story™ 1
    323380 The Magic Circle 1
    965310 The Settlers® : Rise of an Empire - History Edition 1
    898650 The Shapeshifting Detective 1
    352520 The Silent Age 1
    750130 The Sinking City 1
    835960 The Talos Principle 2 1
    1449690 The Walking Dead: The Telltale Definitive Series 1
    1121640 The Wandering Village 1
    443810 This Is the Police 1
    785740 This Is the Police 2 1
    1779200 Thrive 1
    1005930 Timeflow — Life Sim 1
    1184200 Timeflow: Редактор персонажей 1
    368620 Timespinner 1
    1237970 Titanfall® 2 1
    206440 To the Moon 1
    2231380 Tom Clancy's Ghost Recon® Breakpoint 1
    369830 Toonstruck 1
    2555430 TRADESMAN: Deal to Dealer 1
    237930 Transistor 1
    327520 Traverser 1
    394030 Trigonarium 1
    1051200 Trover Saves the Universe 1
    757300 Truberbrook / Trüberbrook 1
    2436050 True Driver 1
    2511290 Two Cubes 1
    1659420 UNCHARTED™: Наследие воров. Коллекция 1
    1203710 UnMetal 1
    2210000 Unnatural: Benighted 1
    2846290 Unspoken 1
    1604030 V Rising 1
    909660 Vagrus - The Riven Realms 1
    1794680 Vampire Survivors 1
    2887680 Vampire Survivors: Operation Guns 1
    338140 Venetica - Gold Edition 1
    857980 Void Bastards 1
    1740680 Voodoo Detective 1
    2005010 Warhammer 40,000: Boltgun 1
    825060 Watch Over Christmas 1
    1807430 Witching Hour 1
    1271460 Woman's body: For adults 3 1
    784150 Workers & Resources: Soviet Republic 1
    888530 Yaga 1
    1462090 Yaga - Roots of Evil 1
    1451180 Yaga Soundtrack 1
    834530 Yakuza Kiwami 1
    334940 Yoku's Island Express 1
    716650 Yorkshire Gubbins 1
    327500 Zenzizenzic 1
    308420 Ziggurat 1
    1159560 Ziggurat 2 1

     

     

Если у меня появятся ещё какие-то скрипты — я размещу их в данной теме.
Если у вас есть идеи по улучшению функционала какого-то магазина (или сайта), то можете написать, я подумаю, можно ли их реализовать.

Изменено пользователем 0wn3df1x
  • Лайк (+1) 1
  • Спасибо (+1) 1
  • +1 1

Поделиться сообщением


Ссылка на сообщение

Скрипт для сбора игр с каталога Steam. || Python

Система фильтрации каталога Steam оставляет желать лучшего. Запросы к базе данных ограничены узкой подборкой:

Скрытый текст
  • Вы можете выбрать стоимость игр только до какого-то предела. Допустим, до 300 рублей, до 150 рублей и так далее. Нельзя установить точные рамки: “от 1000 до 1500”, от “1200 до 1300” и так далее.
  • В Steam есть количество обзоров и рейтинг этих обзоров, но в каталоге нет запроса, который позволил бы оставить только игры с заданных рейтингом и количеством обзоров. Эту проблему можно исправить плагином Augmented Steam, но он не может отправить запрос для получения ограниченной выборки. Вместо этого он просто скрывает игры, которые не попадают под заданные критерии. Таким образом, если вы поставите этот плагин, зададите в нём критерии и начнёте листать магазин — ваш браузер начнёт перегружаться скрытым кодом. Из-за большого количества запросов вы и вовсе можете быть заблокированы на несколько минут — сначала у вас отвалятся картинки и видео, а затем появится надпись о том, что вы не можете получить доступ к сайту.
  • Для того, чтобы увидеть обзоры игры и жанры, к которым она относится, вам нужно навести на неё курсор и дождаться, пока появится виджет со всей информацией. 

Мой скрипт позволяет задать валюту (она же регион), с которого будут собираться данные, а также язык, на котором доступны игры. После чего он соберёт данные обо всех вышедших играх в определённом регионе и доступных на заданном языке и сохранит их в csv файл.

gLY8jlT.png

Скрытый текст
  • app_id — айди игры
  • image_url — картинка игры из магазина
  • title — название игры
  • release_date — дата выхода игры в формате день.месяц.год (есть игры с забагованными датами, которые идут в формате “April 2024” и т.п., они запихиваются в конец таблицы.
  • review_percentage — рейтинг игры.
  • review_count — количество обзоров.
  • tag_ids — основные метки, присвоенные игре (они же жанры).
  • price — текущая цена.
  • orprice — оригинальная цена (пишется только в случае наличия сейчас скидки).
Скрытый текст

9rQIeZv.png

Скрытый текст

Эту таблицу можно загрузить на Google Spreadsheets и там навешать на неё фильтров, после чего фильтровать игры с нужными жанрами, рейтингами, обзорами и ценами. Также можно использовать формулу “image” для выведения картинок. 
Можно добавить столбец, где “https://store.steampowered.com/app/” будет соединяться с идентификатором игры, образуя ссылку.

Скрытый текст

CiHGilP.png

В общем, наличие самой базы данных позволяет извращаться с данными как душе угодно, обходя ограничения сервиса.

Скрытый текст

Инструкция:

  1. Если у вас нет python и для вас это что-то далёкое, то вот готовая сборка программы.
    Скачиваете и запускаете, после чего можете перейти к пункту 9.
     
  2. Допустим, третий python у вас есть.
  3. В консоли перейдите в папку, где будет лежать программа.
  4. В папке создайте файл requirements.txt и напишите в нём
    
    requests
    beautifulsoup4
    aiohttp
  5. Пропишите в консоли
    
    pip install -r requirements.txt

     

  6. Создайте файл config.txt и поместите в него следующие данные:
    
    cc=us
    supportedlang=english
    filename=result.csv
  7. Создайте файл tags.txt и поместите в него следующие данные:
    Скрытый текст
    
    
    9	Strategy
    19	Action
    21	Adventure
    84	Design & Illustration
    87	Utilities
    113	Free to Play
    122	RPG
    128	Massively Multiplayer
    492	Indie
    493	Early Access
    597	Casual
    599	Simulation
    699	Racing
    701	Sports
    784	Video Production
    809	Photo Editing
    872	Animation & Modeling
    1027	Audio Production
    1036	Education
    1038	Web Publishing
    1445	Software Training
    1616	Trains
    1621	Music
    1625	Platformer
    1628	Metroidvania
    1638	Dog
    1643	Building
    1644	Driving
    1645	Tower Defense
    1646	Hack and Slash
    1647	Western
    1649	GameMaker
    1651	Satire
    1654	Relaxing
    1659	Zombies
    1662	Survival
    1663	FPS
    1664	Puzzle
    1665	Match 3
    1666	Card Game
    1667	Horror
    1669	Moddable
    1670	4X
    1671	Superhero
    1673	Aliens
    1674	Typing
    1676	RTS
    1677	Turn-Based
    1678	War
    1680	Heist
    1681	Pirates
    1684	Fantasy
    1685	Co-op
    1687	Stealth
    1688	Ninja
    1695	Open World
    1697	Third Person
    1698	Point & Click
    1702	Crafting
    1708	Tactical
    1710	Surreal
    1714	Psychedelic
    1716	Rogue-like
    1717	Hex Grid
    1718	MOBA
    1719	Comedy
    1720	Dungeon Crawler
    1721	Psychological Horror
    1723	Action RTS
    1730	Sokoban
    1732	Voxel
    1733	Unforgiving
    1734	Fast-Paced
    1736	LEGO
    1738	Hidden Object
    1741	Turn-Based Strategy
    1742	Story Rich
    1743	Fighting
    1746	Basketball
    1751	Comic Book
    1752	Rhythm
    1753	Skateboarding
    1754	MMORPG
    1755	Space
    1756	Great Soundtrack
    1759	Perma Death
    1770	Board Game
    1773	Arcade
    1774	Shooter
    1775	PvP
    1777	Steampunk
    3796	Based On A Novel
    3798	Side Scroller
    3799	Visual Novel
    3810	Sandbox
    3813	Real Time Tactics
    3814	Third-Person Shooter
    3834	Exploration
    3835	Post-apocalyptic
    3839	First-Person
    3841	Local Co-Op
    3843	Online Co-Op
    3854	Lore-Rich
    3859	Multiplayer
    3871	2D
    3877	Precision Platformer
    3878	Competitive
    3916	Old School
    3920	Cooking
    3934	Immersive
    3942	Sci-fi
    3952	Gothic
    3955	Character Action Game
    3959	Rogue-lite
    3964	Pixel Graphics
    3968	Physics
    3978	Survival Horror
    3987	Historical
    3993	Combat
    4004	Retro
    4018	Vampire
    4026	Difficult
    4036	Parkour
    4046	Dragons
    4057	Magic
    4064	Thriller
    4085	Anime
    4094	Minimalist
    4102	Combat Racing
    4106	Action-Adventure
    4115	Cyberpunk
    4136	Funny
    4137	Transhumanism
    4145	Cinematic
    4150	World War II
    4155	Class-Based
    4158	Beat 'em up
    4161	Real-Time
    4166	Atmospheric
    4168	Military
    4172	Medieval
    4175	Realistic
    4182	Singleplayer
    4184	Chess
    4191	3D
    4195	Cartoony
    4202	Trading
    4231	Action RPG
    4234	Short
    4236	Loot
    4242	Episodic
    4252	Stylized
    4255	Shoot 'Em Up
    4291	Spaceships
    4295	Futuristic
    4305	Colorful
    4325	Turn-Based Combat
    4328	City Builder
    4342	Dark
    4345	Gore
    4364	Grand Strategy
    4376	Assassin
    4400	Abstract
    4434	JRPG
    4474	CRPG
    4486	Choose Your Own Adventure
    4508	Co-op Campaign
    4520	Farming
    4559	Quick-Time Events
    4562	Cartoon
    4598	Alternate History
    4604	Dark Fantasy
    4608	Swordplay
    4637	Top-Down Shooter
    4667	Violent
    4684	Wargame
    4695	Economy
    4700	Movie
    4711	Replay Value
    4726	Cute
    4736	2D Fighter
    4747	Character Customization
    4754	Politics
    4758	Twin Stick Shooter
    4777	Spectacle fighter
    4791	Top-Down
    4821	Mechs
    4835	6DOF
    4840	4 Player Local
    4845	Capitalism
    4853	Political
    4878	Parody
    4885	Bullet Hell
    4947	Romance
    4975	2.5D
    4994	Naval Combat
    5030	Dystopian
    5055	eSports
    5094	Narration
    5125	Procedural Generation
    5153	Kickstarter
    5154	Score Attack
    5160	Dinosaurs
    5179	Cold War
    5186	Psychological
    5228	Blood
    5230	Sequel
    5300	God Game
    5310	Games Workshop
    5348	Mod
    5350	Family Friendly
    5363	Destruction
    5372	Conspiracy
    5379	2D Platformer
    5382	World War I
    5390	Time Attack
    5395	3D Platformer
    5407	Benchmark
    5411	Beautiful
    5432	Programming
    5502	Hacking
    5537	Puzzle-Platformer
    5547	Arena Shooter
    5577	RPGMaker
    5608	Emotional
    5611	Mature
    5613	Detective
    5652	Collectathon
    5673	Modern
    5708	Remake
    5711	Team-Based
    5716	Mystery
    5727	Baseball
    5752	Robots
    5765	Gun Customization
    5794	Science
    5796	Bullet Time
    5851	Isometric
    5900	Walking Simulator
    5914	Tennis
    5923	Dark Humor
    5941	Reboot
    5981	Mining
    5984	Drama
    6041	Horses
    6052	Noir
    6129	Logic
    6214	Birds
    6276	Inventory Management
    6310	Diplomacy
    6378	Crime
    6426	Choices Matter
    6506	3D Fighter
    6621	Pinball
    6625	Time Manipulation
    6650	Nudity
    6691	1990's
    6702	Mars
    6730	PvE
    6815	Hand-drawn
    6869	Nonlinear
    6910	Naval
    6915	Martial Arts
    6948	Rome
    6971	Multiple Endings
    7038	Golf
    7107	Real-Time with Pause
    7108	Party
    7113	Crowdfunded
    7178	Party Game
    7208	Female Protagonist
    7250	Linear
    7309	Skiing
    7328	Bowling
    7332	Base Building
    7368	Local Multiplayer
    7423	Sniper
    7432	Lovecraftian
    7478	Illuminati
    7481	Controller
    7569	Grid-Based Movement
    7622	Offroad
    7702	Narrative
    7743	1980s
    7918	Dwarf
    7926	Artificial Intelligence
    7948	Soundtrack
    8013	Software
    8075	TrackIR
    8093	Minigames
    8122	Level Editor
    8253	Music-Based Procedural Generation
    8369	Investigation
    8461	Well-Written
    8666	Runner
    8945	Resource Management
    9130	Hentai
    9157	Underwater
    9204	Immersive Sim
    9271	Trading Card Game
    9541	Demons
    9551	Dating Sim
    9564	Hunting
    9592	Dynamic Narration
    9803	Snow
    9994	Experience
    10235	Life Sim
    10383	Transportation
    10397	Memes
    10437	Trivia
    10679	Time Travel
    10695	Party-Based RPG
    10808	Supernatural
    10816	Split Screen
    11014	Interactive Fiction
    11095	Boss Rush
    11104	Vehicular Combat
    11123	Mouse only
    11333	Villain Protagonist
    11634	Vikings
    12057	Tutorial
    12095	Sexual Content
    12190	Boxing
    12286	Warhammer 40K
    12472	Management
    13070	Solitaire
    13190	America
    13276	Tanks
    13382	Archery
    13577	Sailing
    13782	Experimental
    13906	Game Development
    14139	Turn-Based Tactics
    14153	Dungeons & Dragons
    14720	Nostalgia
    14906	Intentionally Awkward Controls
    15045	Flight
    15172	Conversation
    15277	Philosophical
    15339	Documentary
    15564	Fishing
    15868	Motocross
    15954	Silent Protagonist
    16094	Mythology
    16250	Gambling
    16598	Space Sim
    16689	Time Management
    17015	Werewolves
    17305	Strategy RPG
    17337	Lemmings
    17389	Tabletop
    17770	Asynchronous Multiplayer
    17894	Cats
    17927	Pool
    18594	FMV
    19568	Cycling
    19780	Submarine
    19995	Dark Comedy
    21006	Underground
    21725	Tactical RPG
    21978	VR
    22602	Agriculture
    22955	Mini Golf
    24003	Word Game
    24904	NSFW
    25085	Touch-Friendly
    26921	Political Sim
    27758	Voice Control
    28444	Snowboarding
    29363	3D Vision
    29482	Souls-like
    29855	Ambient
    30358	Nature
    30927	Fox
    31275	Text-Based
    31579	Otome
    32322	Deckbuilding
    33572	Mahjong
    35079	Job Simulator
    42089	Jump Scare
    42329	Coding
    42804	Action Roguelike
    44868	LGBTQ+
    47827	Wrestling
    49213	Rugby
    51306	Foreign
    56690	On-Rails Shooter
    61357	Electronic Music
    65443	Adult Content
    71389	Spelling
    87918	Farming Sim
    91114	Shop Keeper
    92092	Jet
    96359	Skating
    97376	Cozy
    102530	Elf
    117648	8-bit Music
    123332	Bikes
    129761	ATV
    143739	Electronic
    150626	Gaming
    158638	Cricket
    176733	Tile-Matching
    176981	Battle Royale
    180368	Faith
    189941	Instrumental Music
    198631	Mystery Dungeon
    198913	Motorbike
    220585	Colony Sim
    233824	Feature Film
    252854	BMX
    255534	Automation
    323922	Musou
    324176	Hockey
    337964	Rock Music
    348922	Steam Machine
    353880	Looter Shooter
    363767	Snooker
    379975	Clicker
    454187	Traditional Roguelike
    552282	Wholesome
    603297	Hardware
    615955	Idler
    620519	Hero Shooter
    745697	Social Deduction
    769306	Escape Room
    776177	360 Video
    791774	Card Battler
    847164	Volleyball
    856791	Asymmetric VR
    916648	Creature Collector
    922563	Roguevania
    1003823	Profile Features Limited
    1023537	Boomer Shooter
    1084988	Auto Battler
    1091588	Roguelike Deckbuilder
    1100686	Outbreak Sim
    1100687	Automobile Sim
    1100688	Medical Sim
    1100689	Open World Survival Craft
    1199779	Extraction Shooter
    1220528	Hobby Sim
    1254546	Football (Soccer)
    1254552	Football (American)

     

  8. Создайте файл start.py и поместите в него основной код скрипта
    Скрытый текст
    
    
    import tkinter as tk
    from tkinter import messagebox
    import requests
    from bs4 import BeautifulSoup
    import csv
    import asyncio
    import aiohttp
    import re
    import os
    import threading
    from datetime import datetime
    import time
    
    # Функция для загрузки данных о тегах из файла
    def load_tags():
        tags = {}
        with open('tags.txt', 'r', encoding='utf-8') as file:
            for line in file:
                tag_id, tag_text = line.strip().split('\t')
                tags[tag_id] = tag_text
        return tags
    
    # Функция для извлечения данных из элемента
    def extract_data(element, tags):
        app_id = element.get('data-ds-appid', '')
        title = element.find(class_='title').text.strip() if element.find(class_='title') else ''
        tag_ids = element.get('data-ds-tagids', '')
        tag_ids = tag_ids.strip('[]')  # Удаляем квадратные скобки из строки
        tag_ids = [tags.get(tag_id, '') for tag_id in tag_ids.split(',')]
        tooltip_html = element.find(class_='search_review_summary')['data-tooltip-html'] if element.find(class_='search_review_summary') else ''
        review_percentage = re.search(r'(\d+)%', tooltip_html).group(1) if tooltip_html else ''
        review_count = re.search(r'of the (\d{1,3}(?:,\d{3})*)', tooltip_html).group(1) if tooltip_html else ''
        review_count = review_count.replace(',', '')  # Убираем запятые из числа обзоров
        image_url = element.find('img')['src'] if element.find('img') else ''
        release_date = element.find(class_='search_released').text.strip() if element.find(class_='search_released') else ''
        release_date = parse_date(release_date)  # Преобразуем дату в единый формат
        orprice = element.find(class_='discount_original_price').text.strip() if element.find(class_='discount_original_price') else 'No price'
        price = element.find(class_='discount_final_price').text.strip() if element.find(class_='discount_final_price') else 'No price'
    
        return [app_id, image_url, title, release_date, review_percentage, review_count, tag_ids, price, orprice]
    
    # Функция для преобразования даты в единый формат
    def parse_date(date_str):
        if not date_str or date_str == 'Coming Soon':
            return date_str
        try:
            date_obj = datetime.strptime(date_str, '%b %d, %Y')
        except ValueError:
            try:
                date_obj = datetime.strptime(date_str, '%d %b, %Y')
            except ValueError:
                return date_str
        return date_obj.strftime('%d.%m.%Y')
    
    # Функция для получения количества страниц
    def get_total_pages(url):
        response = requests.get(url)
        soup = BeautifulSoup(response.text, 'html.parser')
        pagination = soup.find(class_='search_pagination_right')
        if pagination:
            pages = pagination.find_all('a')
            if pages:
                return int(pages[-2].text)
        return 1
    
    # Асинхронная функция для сбора данных
    async def fetch_data(session, url, tags, writer, progress_var, total_pages, start_time, speed_var, time_left_var):
        try:
            async with session.get(url) as response:
                response.raise_for_status()
                soup = BeautifulSoup(await response.text(), 'html.parser')
                elements = soup.find_all(class_='search_result_row')
                for element in elements:
                    data = extract_data(element, tags)
                    writer.writerow(data)
                progress_var.set(progress_var.get() + 1)
                update_progress_label(progress_var, total_pages, start_time, speed_var, time_left_var)
        except (aiohttp.ClientError, asyncio.TimeoutError) as e:
            print(f"Error fetching {url}: {e}")
            await asyncio.sleep(30)
            await fetch_data(session, url, tags, writer, progress_var, total_pages, start_time, speed_var, time_left_var)
    
    # Асинхронная функция для запуска сбора данных
    async def start_collecting(cc, supportedlang, filename, progress_var, speed_var, time_left_var):
        tags = load_tags()
        total_pages = get_total_pages(f'https://store.steampowered.com/search/?sort_by=Released_DESC&ignore_preferences=1&category1=998&cc={cc}&supportedlang={supportedlang}&page=1&ndl=1')
        progress_var.set(0)
        temp_filename = filename + '.tmp'
        with open(temp_filename, 'w', newline='', encoding='utf-8') as csvfile:
            writer = csv.writer(csvfile, delimiter='\t')
            writer.writerow(['app_id', 'image_url', 'title', 'release_date', 'review_percentage', 'review_count', 'tag_ids', 'price', 'orprice'])
            async with aiohttp.ClientSession() as session:
                start_time = time.time()
                tasks = [fetch_data(session, f'https://store.steampowered.com/search/?sort_by=Released_DESC&ignore_preferences=1&category1=998&cc={cc}&supportedlang={supportedlang}&page={page}&ndl=1', tags, writer, progress_var, total_pages, start_time, speed_var, time_left_var) for page in range(1, total_pages + 1)]
                await asyncio.gather(*tasks)
        
        # Сортировка данных по дате
        sort_data_by_date(temp_filename, filename)
        os.remove(temp_filename)
        messagebox.showinfo("Сбор завершён", "Сбор данных завершен.")
    
    # Функция для сортировки данных по дате
    def sort_data_by_date(temp_filename, filename):
        data = []
        with open(temp_filename, 'r', encoding='utf-8') as csvfile:
            reader = csv.reader(csvfile, delimiter='\t')
            header = next(reader)
            for row in reader:
                data.append(row)
        
        # Сортировка данных по дате, помещая ошибочные даты в конец
        data.sort(key=lambda x: (is_valid_date(x[3]), datetime.strptime(x[3], '%d.%m.%Y') if is_valid_date(x[3]) else datetime.min), reverse=True)
        
        with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
            writer = csv.writer(csvfile, delimiter='\t')
            writer.writerow(header)
            writer.writerows(data)
    
    # Функция для проверки, является ли дата валидной
    def is_valid_date(date_str):
        try:
            datetime.strptime(date_str, '%d.%m.%Y')
            return True
        except ValueError:
            return False
    
    # Функция для сохранения конфигурации
    def save_config(cc, supportedlang, filename):
        with open('config.txt', 'w', encoding='utf-8') as file:
            file.write(f'cc={cc}\nsupportedlang={supportedlang}\nfilename={filename}')
    
    # Функция для загрузки конфигурации
    def load_config():
        if os.path.exists('config.txt'):
            with open('config.txt', 'r', encoding='utf-8') as file:
                config = {}
                for line in file:
                    key, value = line.strip().split('=')
                    config[key] = value
                return config
        return {'cc': 'us', 'supportedlang': 'russian', 'filename': 'result'}
    
    # Функция для запуска сбора данных в отдельном потоке
    def start_collecting_thread(cc, supportedlang, filename, progress_var, speed_var, time_left_var):
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        loop.run_until_complete(start_collecting(cc, supportedlang, filename, progress_var, speed_var, time_left_var))
        loop.close()
    
    # Функция для обновления метки прогресса
    def update_progress_label(progress_var, total_pages, start_time, speed_var, time_left_var):
        current_time = time.time()
        elapsed_time = current_time - start_time
        speed = progress_var.get() / elapsed_time if elapsed_time > 0 else 0
        speed_var.set(f"{speed:.2f} стр/сек")
        remaining_pages = total_pages - progress_var.get()
        remaining_time = remaining_pages / speed if speed > 0 else 0
        minutes, seconds = divmod(remaining_time, 60)
        time_left_var.set(f"Осталось {int(minutes)} м. {int(seconds)} сек.")
        progress_label.config(text=f"{progress_var.get()}/{total_pages}")
    
    # Функция для запуска сбора данных
    def on_start_collecting():
        cc = cc_entry.get()
        supportedlang = supportedlang_entry.get()
        filename = filename_entry.get()
        save_config(cc, supportedlang, filename)
        threading.Thread(target=start_collecting_thread, args=(cc, supportedlang, filename, progress_var, speed_var, time_left_var)).start()
    
    # Создание главного окна
    root = tk.Tk()
    root.title("Steam Data Collector")
    
    # Загрузка конфигурации
    config = load_config()
    
    # Создание элементов интерфейса
    tk.Label(root, text="Валюта").grid(row=0, column=0)
    cc_entry = tk.Entry(root)
    cc_entry.insert(0, config['cc'])
    cc_entry.grid(row=0, column=1)
    
    tk.Label(root, text="Язык").grid(row=1, column=0)
    supportedlang_entry = tk.Entry(root)
    supportedlang_entry.insert(0, config['supportedlang'])
    supportedlang_entry.grid(row=1, column=1)
    
    tk.Label(root, text="Название файла").grid(row=2, column=0)
    filename_entry = tk.Entry(root)
    filename_entry.insert(0, config['filename'])
    filename_entry.grid(row=2, column=1)
    
    start_button = tk.Button(root, text="Начать сбор", command=on_start_collecting)
    start_button.grid(row=3, column=0, columnspan=2)
    
    progress_var = tk.IntVar()
    progress_label = tk.Label(root, text="0/0")
    progress_label.grid(row=4, column=0, columnspan=2)
    
    speed_var = tk.StringVar()
    speed_label = tk.Label(root, textvariable=speed_var)
    speed_label.grid(row=5, column=0, columnspan=2)
    
    time_left_var = tk.StringVar()
    time_left_label = tk.Label(root, textvariable=time_left_var)
    time_left_label.grid(row=6, column=0, columnspan=2)
    
    # Запуск главного цикла
    root.mainloop()

     

  9. Запустите программу с помощью
    
    python start.py
  10. В открывшемся окне:
    - Измените валюту/регион на нужную вам. (us — США; ru — Россия; kz — Казахстан) 
    - Измените язык на нужный вам (english — будут показаны игры, где есть английский; russian — будут показаны игры, где есть русский)
    - Можете изменить название файла, если хотите.
    rSN0VE3.png
     
  11. После этого можете нажать на кнопку “Начать сбор” и через несколько секунд начнётся сбор информации.
  12. Во время сбора вам будет виден прогресс выполнения, скорость загрузки страниц и оставшееся время. В конце появится табличка, что сбор завершён. После этого можно закрывать программу.
  13. Файл в виде csv таблицы хранится в папке с программой. Можете импортировать её на гугл-таблицы или в любое табличное приложение (разделитель — табуляция).
     

(!) Желательно собирать информацию под прокси или vpn, чтобы ваш родной айпишник не заблокировало на несколько минут за частое обращение к серверу. 

(!) Если вы собираете слишком большой объём данных, к примеру, собираете все игры из американского региона на английском языке (us+english), что предусматривает сбор информации с 3207 страниц, то примерно на 2000-й странице консоль начнёт сыпать ошибками, а сам скрипт перестанет собирать информацию на 30 секунд. Через 30 секунд он возобновит сбор информации. Вы можете воспользоваться этим моментом, чтобы сменить прокси и избежать временной блокировки. Скрипт соберёт пропущенную информацию в любом случае.

(!) При сборе небольшого объёма информации, к примеру, всех игр на русском языке в любом регионе, ошибок и временных блокировок возникнуть не должно.

Данный скрипт неоднократно использовался мной для составления разной статистики, а также для того, чтобы узнавать о появлении официальных переводов для уже вышедших игр путём сравнения сохранённых баз.

Также он удобен для ведения бэклога. В своей userdata вы можете раздобыть информацию об играх из своего списка желаемого и играх, которые есть на аккаунте, после чего воспользоваться vlookup, чтобы отметить их на таблице. Это позволит отфильтровать игры, которые у вас есть и которые вы уже желаете, чтобы найти что-нибудь новое и пометить. Также можно пометить игры, которые вы уже изучили, чтобы в дальнейшем их скрывать. 

Изменено пользователем 0wn3df1x

Поделиться сообщением


Ссылка на сообщение

Обновление: Объединение и улучшение скриптов для раздела Sales на SteamDB.

Скрипты “SteamDB - Sales; Фильтр Недоступных” и “Steam DB — Sales; конвертация тенге в рубли” под TamperMonkey были объединены и улучшены:

  1. В рамках “фильтра недоступных” было добавлено поле с календарём для выборы даты в формате “день.месяц.год” и кнопка “отсеять по дате”. В чём польза этой функции? Периодически в Steam появляются новые скидки, но функционал SteamDB не позволяет выставить дату появления скидок. А значит, если вы отфильтруете таблицу по размеру скидок, то увидите не только новые, но и кучу старых. Данная фунция это исправляет и позволяет оставить только скидки, появившиеся после заданной даты.
  2. В рамках “конвертации” произошло расширение списка валют. Теперь конвертировать можно не только тенге. но и любую представленную на SteamDB валюту. Требуется только вбить курс валюты по отношению к вашей и нажать “конвертировать”. 
  3. Теперь оба скрипта объединены в “SteamDB - Sales; Улучшатор”.

Предназначение скрипта: На SteamDB есть раздел Sales, который позволяет посмотреть и отфильтровать актуальные скидки. Если кто-то смотрит цены для Казахстана или США, то ему необходимо каждый раз в голове конвертировать их в свою валюту. Данный скрипт позволяет задать курс и конвертировать любую валюту в другую валюту. Также данный скрипт позволяет увидеть, какие игры на распродаже недоступны в каком-то регионе. Ещё он позволяет увидеть скидки, появившиеся после заданной даты.

gkujek0.png
 

Скрытый текст

// ==UserScript==
// @name         SteamDB - Sales; Улучшатор
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  Добавляет списки для фильтрации, фильтр по дате начала скидки и конвертацию валют
// @author       0wn3df1x
// @match        https://steamdb.info/sales/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Создание кнопки
    function createButton(text, onClick) {
        const button = document.createElement('button');
        button.textContent = text;
        button.style.margin = '5px';
        button.addEventListener('click', onClick);
        button.classList.add('steam-button'); // Добавляем класс для стилизации
        return button;
    }

    // Сбор всех ID
    function getAppIds() {
        const appIds = [];
        const rows = document.querySelectorAll('tr.app');
        rows.forEach(row => {
            const appId = row.getAttribute('data-appid');
            if (appId) {
                appIds.push(appId);
            }
        });
        return appIds;
    }

    // Функция для скрытия строк, которые есть в обоих списках
    function filterTable() {
        const list1 = JSON.parse(localStorage.getItem('list1') || '[]');
        const list2 = JSON.parse(localStorage.getItem('list2') || '[]');
        const commonAppIds = list1.filter(id => list2.includes(id));

        const rows = document.querySelectorAll('tr.app');
        rows.forEach(row => {
            const appId = row.getAttribute('data-appid');
            if (commonAppIds.includes(appId)) {
                row.style.display = 'none';
            } else {
                row.style.display = '';
            }
        });
    }

    // Функция для фильтрации по дате начала скидки
    function filterByDate() {
        const selectedDate = document.getElementById('datePicker').value;
        if (!selectedDate) {
            alert('Пожалуйста, выберите дату.');
            return;
        }

        const selectedTimestamp = new Date(selectedDate).getTime() / 1000;

        const rows = document.querySelectorAll('tr.app');
        rows.forEach(row => {
            const timeagoElements = row.querySelectorAll('td.timeago.dt-type-numeric');
            let startTimestamp;
            if (timeagoElements.length > 1) {
                startTimestamp = parseInt(timeagoElements[1].getAttribute('data-sort'), 10);
            } else if (timeagoElements.length === 1) {
                startTimestamp = parseInt(timeagoElements[0].getAttribute('data-sort'), 10);
            } else {
                startTimestamp = 0;
            }

            if (startTimestamp < selectedTimestamp) {
                row.style.display = 'none';
            } else {
                row.style.display = '';
            }
        });
    }

    // Стандартный курс валюты к выбранной валюте
    const defaultExchangeRate = 1;

    // Функция для конвертации цены из любой валюты в выбранную валюту
    function convertPrice(priceInSourceCurrency, exchangeRate) {
        const priceInTargetCurrency = priceInSourceCurrency * exchangeRate;
        return priceInTargetCurrency.toFixed(2); // Округляем до двух знаков после запятой
    }

    // Функция для добавления кнопки и текстового поля
    function addConvertButtonAndInput() {
        const headerSubtitle = document.querySelector('h2.header-subtitle');
        if (headerSubtitle) {
            const container = document.createElement('div');
            container.classList.add('convert-container');

            const input = document.createElement('input');
            input.type = 'text';
            input.value = defaultExchangeRate;
            input.classList.add('exchange-rate-input');

            const button = document.createElement('button');
            button.textContent = 'Конвертировать';
            button.classList.add('convert-button');
            button.addEventListener('click', () => {
                const exchangeRate = parseFloat(input.value);
                if (!isNaN(exchangeRate)) {
                    convertPrices(exchangeRate);
                }
            });

            container.appendChild(input);
            container.appendChild(button);
            headerSubtitle.parentNode.insertBefore(container, headerSubtitle.nextSibling);
        }
    }

    // Функция для конвертации цен
    function convertPrices(exchangeRate) {
        const appRows = document.querySelectorAll('tr.app');

        appRows.forEach(row => {
            const priceElements = row.querySelectorAll('td.dt-type-numeric');
            if (priceElements.length >= 3) {
                const priceElement = priceElements[2]; // Третий элемент (индексация с 0)
                const priceText = priceElement.textContent.trim();

                // Проверка на Peruvian sol
                if (priceText.includes('S/.')) {
                    const priceMatch = priceText.match(/S\/\.([0-9,.]+)/);
                    if (priceMatch) {
                        const priceInSourceCurrency = parseFloat(priceMatch[1].replace(',', '.'));
                        if (!isNaN(priceInSourceCurrency)) {
                            const priceInTargetCurrency = convertPrice(priceInSourceCurrency, exchangeRate);
                            priceElement.textContent = priceInTargetCurrency;
                        }
                    }
                } else {
                    const priceMatch = priceText.match(/([0-9,.]+)/); // Упрощенное регулярное выражение
                    if (priceMatch) {
                        const priceInSourceCurrency = parseFloat(priceMatch[1].replace(',', '.'));
                        if (!isNaN(priceInSourceCurrency)) {
                            const priceInTargetCurrency = convertPrice(priceInSourceCurrency, exchangeRate);
                            priceElement.textContent = priceInTargetCurrency;
                        }
                    }
                }
            }
        });
    }

    // Добавление кнопок и элементов управления
    const headerSubtitle = document.querySelector('h2.header-subtitle');
    if (headerSubtitle) {
        const list1Button = createButton('Список 1', () => {
            const appIds = getAppIds();
            localStorage.setItem('list1', JSON.stringify(appIds));
            alert('Список 1 сохранен');
        });
        headerSubtitle.appendChild(list1Button);

        const list2Button = createButton('Список 2', () => {
            const appIds = getAppIds();
            localStorage.setItem('list2', JSON.stringify(appIds));
            alert('Список 2 сохранен');
        });
        headerSubtitle.appendChild(list2Button);

        const filterButton = createButton('Фильтр', () => {
            filterTable();
            alert('Таблица отфильтрована');
        });
        headerSubtitle.appendChild(filterButton);

        const datePicker = document.createElement('input');
        datePicker.type = 'date';
        datePicker.id = 'datePicker';
        datePicker.style.margin = '5px';
        headerSubtitle.appendChild(datePicker);

        const filterByDateButton = createButton('Отсеять по дате', () => {
            filterByDate();
            alert('Таблица отфильтрована по дате');
        });
        headerSubtitle.appendChild(filterByDateButton);

        addConvertButtonAndInput();
    }

    // Добавление стилей CSS
    const style = document.createElement('style');
    style.textContent = `
        .steam-button {
            background-color: #171a21;
            color: #c7d5e0;
            border: 1px solid #616c77;
            padding: 5px 10px;
            border-radius: 3px;
            transition: background-color 0.3s, color 0.3s;
        }
        .steam-button:hover {
            background-color: #616c77;
            color: #ffffff;
        }
        .convert-container {
            display: flex;
            align-items: center;
        }
        .exchange-rate-input {
            margin-right: 10px;
            padding: 5px;
            border: 1px solid #4a698a;
            border-radius: 3px;
        }
        .convert-button {
            background-color: #17202f;
            color: #acdbf5;
            border: 1px solid #4a698a;
            padding: 5px 10px;
            border-radius: 3px;
            cursor: pointer;
            transition: background-color 0.3s, color 0.3s;
        }
        .convert-button:hover {
            background-color: #4a698a;
            color: #ffffff;
        }
    `;
    document.head.appendChild(style);
})();

 

Скрытый текст
  1. Заходим на страницу Sales на SteamDB.
  2. Проставляем нужную нам валюту и нужные нам фильтры, если необходимо.
  3. В выпадающем списке entries per page выбираем All (slow) и ждём, пока все предложения прогрузятся.
    siKdGDV.png
  4. Вбиваем нужный курс в текстовое поле и нажимаем “конвертировать”. Вуаля:
    YZ3ilwG.png

P.S. В поле const defaultExchangeRate = скрипта находится базовый курс. Можете заменить его на курс актуальный для вас валюты, чтобы при обновлении страницы видеть его.

Скрытый текст
  1. Заходим на страницу Sales на SteamDB.
  2. Проставляем валюту “рубли” (или любую другую) и нужные нам фильтры, если необходимо.
  3. В выпадающем списке entries per page выбираем All (slow) и ждём, пока все предложения прогрузятся.siKdGDV.png
  4. Жмём на кнопку “Список 1” и получаем уведомление, что список сохранён.
    UA0OQ9J.png
     
  5. Проставляем валюту. Снова в выпадающем списке entries per page выбираем All (slow) и ждём, пока все предложения прогрузятся. После чего жмём кнопку “Список 2”, чтобы сохранить все игры из казахского региона.
  6. После этого можем нажать кнопку фильтр. Сперва вам покажется, что всё чуть подвисло и съехало, но затем все позиции прогрузятся. Останутся только игры, которых нет в одном из списков.
    3xur1Ex.png
     
  7. Обновление (16.07.2024): Вы можете выбрать дату, нажав на символ календаря или просто вбив её вручную, а затем нажать “отсеять по дате”, чтобы вам были показаны игры, скидка на которые появилась после этой даты.
    (Важно, чтобы entries per page стояла на “All (slow)”
    kOp58sS.png

P.S. Скрипт прекрасно работает с конвертацией тенге, так что можно применять их одновременно.
P.S.2: Если после этого вы выберете рубли, снова проставите “All (Slow)” и снова нажмете фильтр — увидите игры, которые почему-то не показываются для Казахстана.

4utM6ip.png

Как вы можете понять из обеих инструкций, главное в выпадающем списке entries per page выбираем All (slow).
Всё универсально и доступно.

Изменено пользователем 0wn3df1x

Поделиться сообщением


Ссылка на сообщение

Создайте аккаунт или войдите в него для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать аккаунт

Зарегистрируйтесь для получения аккаунта. Это просто!

Зарегистрировать аккаунт

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас



Zone of Games © 2003–2024 | Реклама на сайте.

×