Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- (async () => {
- // --- НАСТРОЙКИ ---
- // Задержка между запросами к страницам (в миллисекундах)
- const DELAY_BETWEEN_REQUESTS = 1000; // 1 секунда
- const PAGINATION_SELECTOR = 'p.small a.pg'; // Селектор для пагинации
- const TOPIC_SELECTOR = 'a.torTopic'; // Селектор для тем
- // -----------------
- console.log('%cЗапуск парсера тегов...', 'color: #28a745; font-size: 16px; font-weight: bold;');
- /**
- * Фильтрует и нормализует тег.
- * @param {string} tag - Исходная строка тега.
- * @returns {string|null} - Возвращает очищенный тег или null, если тег нужно отбросить.
- */
- const normalizeAndFilterTag = (tag) => {
- let cleanedTag = tag.trim().replace(/\s*<wbr>|/g, '');
- const BANNED_PATTERNS = [
- /^\d{4}(\s*г\.?)?$/, // Годы: 2025, 1998 г.
- /^v?(\d+\.)+[\w\s.-]*$/, // Версии: v0.1.2, 1.0, 0.5b, 1.0 Beta
- /^\d+(\.\d+)?$/, // Числа: 0.21, 1.0, 5
- /^(ch|ep|act|vol|part|s|season|день)\s*\.?\s*\d+.*/i, // Маркеры: Ch. 1, Ep. 5
- /^(rus|eng|jap|chi|kor|multi|ger|fra|ita|esp)$/i, // Языки
- /^(uncen|cen|ptcen)$/i, // Цензура
- /^(cd|dvd|iso|apk)$/i, // Типы файлов/носителей
- /\b(walkthrough|save|mod|dlc|fix|beta|alpha|demo|final|extra|repack|steam|gog|rus|eng)\b/i // Служебные слова
- ];
- for (const pattern of BANNED_PATTERNS) {
- if (pattern.test(cleanedTag)) return null;
- }
- if (cleanedTag.length < 2) return null;
- // Нормализация регистра, сохраняя акронимы
- if (cleanedTag.toUpperCase() === cleanedTag) {
- return cleanedTag; // 'RPG', 'BDSM'
- }
- if (cleanedTag.toLowerCase() === 'jrpg') {
- return 'jRPG';
- }
- return cleanedTag.charAt(0).toUpperCase() + cleanedTag.slice(1).toLowerCase();
- };
- /**
- * Извлекает теги со страницы и добавляет их в общую карту уникальных тегов.
- * @param {string} htmlText - HTML-код страницы.
- * @param {Map<string, string>} uniqueTagsMap - Карта для хранения уникальных тегов.
- * @returns {number} - Количество новых добавленных тегов.
- */
- const extractTagsFromHtml = (htmlText, uniqueTagsMap) => {
- const parser = new DOMParser();
- const doc = parser.parseFromString(htmlText, 'text/html');
- const topicLinks = doc.querySelectorAll(TOPIC_SELECTOR);
- let newTagsCount = 0;
- topicLinks.forEach(link => {
- const title = link.textContent;
- const matches = title.match(/\[(.*?)\]/g);
- if (matches) {
- matches.forEach(match => {
- const content = match.slice(1, -1).trim();
- if (content.includes(',')) {
- content.split(',').forEach(rawTag => {
- const processedTag = normalizeAndFilterTag(rawTag);
- if (processedTag) {
- const key = processedTag.toLowerCase();
- if (!uniqueTagsMap.has(key)) {
- uniqueTagsMap.set(key, processedTag);
- newTagsCount++;
- }
- }
- });
- }
- });
- }
- });
- return newTagsCount;
- };
- /**
- * Генерирует полный список URL всех страниц пагинации.
- * @returns {string[]} - Массив URL.
- */
- const getAllPageUrls = () => {
- const pageLinks = Array.from(document.querySelectorAll(PAGINATION_SELECTOR));
- let maxPage = 1;
- let itemsPerPage = 50;
- let baseUrl = window.location.href;
- pageLinks.forEach(link => {
- const pageNum = parseInt(link.textContent, 10);
- if (!isNaN(pageNum) && pageNum > maxPage) maxPage = pageNum;
- });
- if (maxPage > 1) {
- const firstLinkWithStart = pageLinks.find(link => new URL(link.href).searchParams.has('start'));
- if (firstLinkWithStart) {
- const sampleUrl = new URL(firstLinkWithStart.href);
- const startValue = parseInt(sampleUrl.searchParams.get('start'), 10);
- const pageNum = parseInt(firstLinkWithStart.textContent, 10);
- if (pageNum > 1) itemsPerPage = startValue / (pageNum - 1);
- sampleUrl.searchParams.delete('start');
- baseUrl = sampleUrl.href;
- }
- }
- const allUrls = [];
- const baseUrlObj = new URL(baseUrl);
- baseUrlObj.searchParams.delete('start');
- for (let i = 1; i <= maxPage; i++) {
- const url = new URL(baseUrlObj.href);
- const start = (i - 1) * itemsPerPage;
- if (start > 0) url.searchParams.set('start', start);
- allUrls.push(url.href);
- }
- console.log(`Обнаружено ${maxPage} страниц. Генерируем все ссылки для анализа.`);
- return allUrls;
- };
- /**
- * Основная функция-исполнитель.
- */
- const main = async () => {
- const allPages = getAllPageUrls();
- const uniqueTags = new Map();
- let processedPages = 0;
- for (const url of allPages) {
- try {
- console.log(`Обработка страницы ${processedPages + 1} из ${allPages.length}: ${url}`);
- const response = await fetch(url);
- if (!response.ok) throw new Error(`Ошибка сети: ${response.statusText}`);
- const htmlText = await response.text();
- const newTagsCount = extractTagsFromHtml(htmlText, uniqueTags);
- processedPages++;
- console.log(`Найдено ${newTagsCount} новых уникальных тегов. Всего: ${uniqueTags.size}`);
- if (processedPages < allPages.length) {
- await new Promise(resolve => setTimeout(resolve, DELAY_BETWEEN_REQUESTS));
- }
- } catch (error) {
- console.error(`Не удалось обработать страницу ${url}:`, error);
- }
- }
- console.log('%c====================================', 'color: #007bff; font-weight: bold;');
- console.log(`%cОбработка завершена! Найдено ${uniqueTags.size} уникальных тегов.`, 'color: #28a745; font-size: 16px; font-weight: bold;');
- const sortedTags = Array.from(uniqueTags.values()).sort((a, b) => a.localeCompare(b));
- const resultString = sortedTags.join(', ');
- console.log('%cРезультат (отсортирован и готов к копированию):', 'color: #17a2b8; font-size: 14px;');
- console.log(resultString);
- try {
- await navigator.clipboard.writeText(resultString);
- console.log('%cРезультат скопирован в буфер обмена!', 'color: #28a745; font-weight: bold;');
- } catch (err) {
- console.error('Не удалось автоматически скопировать текст:', err);
- }
- };
- await main();
- })();
Add Comment
Please, Sign In to add comment