From fa033c8833ed77a81ee67605cbdcce9482406e75 Mon Sep 17 00:00:00 2001 From: Herzic Date: Mon, 16 Mar 2026 12:58:57 +0300 Subject: [PATCH] feat: Add collect_promo_links --- parse/collect_promo_links.js | 207 +++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 parse/collect_promo_links.js diff --git a/parse/collect_promo_links.js b/parse/collect_promo_links.js new file mode 100644 index 0000000..660fd29 --- /dev/null +++ b/parse/collect_promo_links.js @@ -0,0 +1,207 @@ +// ==UserScript== +// @name Yandex Promo Links Collector (Multi-Query Console Version) +// @namespace yandex.console.collector.promo.multi +// @version 1.2 +// @description Собирает промо ссылки из выдачи Яндекса для нескольких запросов — версия для Tampermonkey (единый вывод) +// @match https://yandex.ru/search/* +// @match https://www.yandex.ru/search/* +// @match https://yandex.com/search/* +// @grant GM_xmlhttpRequest +// @connect yandex.ru +// @connect yandex.com +// @run-at document-idle +// ==/UserScript== +(function() { + 'use strict'; + // === НАСТРОЙКИ === + const QUERIES = [ + 'купить Great Wall в Краснодаре', + 'купить BYD в Краснодаре', + 'купить SAIC в Краснодаре', + 'купить BAIC в Краснодаре', + 'купить Lifan в Краснодаре', + 'купить Zotye в Краснодаре', + 'купить Brilliance в Краснодаре', + 'купить Hongqi в Краснодаре', + 'купить Tank в Краснодаре', + 'купить Lynk & Co в Краснодаре', + 'купить Zeekr в Краснодаре', + 'купить Nio в Краснодаре', + 'купить Xpeng в Краснодаре', + 'купить Li Auto в Краснодаре', + 'купить Seres в Краснодаре', + 'купить Aion в Краснодаре', + 'купить Forthing в Краснодаре', + 'купить Dayun в Краснодаре', + 'купить авто с пробегом в Краснодаре', + 'купить б/у автомобиль в Краснодаре', + 'подержанные машины в Краснодаре', + 'автосалоны с пробегом в Краснодаре', + 'купить подержанный авто в Краснодаре', + 'б/у Haval в Краснодаре', + 'б/у Chery в Краснодаре', + 'б/у Geely в Краснодаре', + 'б/у Changan в Краснодаре', + 'б/у Exeed в Краснодаре', + 'б/у Jetour в Краснодаре', + 'б/у GAC в Краснодаре', + 'б/у Jaecoo в Краснодаре', + 'б/у FAW в Краснодаре', + 'б/у DongFeng в Краснодаре', + 'б/у JAC в Краснодаре', + 'б/у Kaiyi в Краснодаре', + 'б/у BAIC в Краснодаре', + 'б/у Lifan в Краснодаре', + 'б/у Hongqi в Краснодаре', + 'б/у Tank в Краснодаре', + 'б/у Nio в Краснодаре', + 'б/у Xpeng в Краснодаре', + 'автосалоны подержанных авто в Краснодаре', + 'купить машину с пробегом от дилера в Краснодаре', + 'trade-in авто в Краснодаре', + 'обмен авто с пробегом в Краснодаре', + 'сертифицированные б/у автомобили в Краснодаре' + ]; + const MAX_PAGES_PER_QUERY = 3;// Макс. страниц на запрос + const PAGE_DELAY_MS = 500;// Задержка между страницами + const QUERY_DELAY_MS = 1000;// Задержка между запросами (чтобы не блочить) + // ================= + var allLinks = []; + var uniqueLinks = new Set(); + var baseUrl = window.location.origin; + // === ФУНКЦИИ === + function cleanUrl(url) { + try { + var urlObj = new URL(url); + urlObj.searchParams.delete('primary_reqid'); + urlObj.searchParams.delete('rnd'); + urlObj.searchParams.delete('reqid'); + return urlObj.toString(); + } catch (e) { + return url; + } + } + function processPage(doc, queryLinks) { + var nodes = doc.querySelectorAll('li.serp-item a[href]'); + var pageLinks = []; + for (var i = 0; i < nodes.length; i++) { + var href = nodes[i].getAttribute('href'); + if (href && href.startsWith('https://yabs.yandex.ru')) { + var fullUrl = href.startsWith('http') ? href : baseUrl + href; + var cleanedUrl = cleanUrl(fullUrl); + if (!uniqueLinks.has(cleanedUrl)) { + uniqueLinks.add(cleanedUrl); + pageLinks.push(cleanedUrl); + queryLinks.push(cleanedUrl); + } + } + } + return pageLinks.length; + } + function getNextPageUrl(doc) { + var nextBtn = doc.querySelector('a.Pager-Item_type_next'); + if (!nextBtn) { + nextBtn = doc.querySelector('a[aria-label="Следующая страница"]'); + } + if (nextBtn) { + var href = nextBtn.getAttribute('href'); + if (href) { + return href.startsWith('http') ? href : baseUrl + href; + } + } + return null; + } + async function processQuery(query) { + var queryLinks = []; + var pagesProcessed = 0; + var searchUrl = `${baseUrl}/search/?text=${encodeURIComponent(query)}`; + try { + var html = await gmFetch(searchUrl); + var parser = new DOMParser(); + var doc = parser.parseFromString(html, 'text/html'); + processPage(doc, queryLinks); + pagesProcessed++; + var nextUrl = getNextPageUrl(doc); + while (nextUrl && pagesProcessed < MAX_PAGES_PER_QUERY) { + await new Promise(r => setTimeout(r, PAGE_DELAY_MS)); + html = await gmFetch(nextUrl); + doc = parser.parseFromString(html, 'text/html'); + processPage(doc, queryLinks); + pagesProcessed++; + nextUrl = getNextPageUrl(doc); + } + console.log(`[Запрос: "${query}"] Обработано страниц: ${pagesProcessed}, промо ссылок: ${queryLinks.length}`); + allLinks.push(queryLinks.join('\n')); + } catch (error) { + console.error(`❌ Ошибка для запроса "${query}":`, error); + } + return queryLinks.length; + } + // === TAMPERMONKEY-ОБЁРТКИ === + function gmFetch(url) { + return new Promise((resolve, reject) => { + GM_xmlhttpRequest({ + method: 'GET', + url: url, + headers: { + 'User-Agent': navigator.userAgent, + 'Referer': baseUrl + }, + onload: (response) => { + if (response.status >= 200 && response.status < 300) { + resolve(response.responseText); + } else { + reject(new Error(`HTTP ${response.status}`)); + } + }, + onerror: (err) => reject(err), + ontimeout: (err) => reject(new Error('Timeout')) + }); + }); + } + function copyToClipboard(text) { + if (navigator.clipboard && window.isSecureContext) { + return navigator.clipboard.writeText(text); + } + return new Promise((resolve, reject) => { + try { + var textarea = document.createElement('textarea'); + textarea.value = text; + textarea.style.position = 'fixed'; + textarea.style.left = '-9999px'; + document.body.appendChild(textarea); + textarea.focus(); + textarea.select(); + var success = document.execCommand('copy'); + document.body.removeChild(textarea); + success ? resolve() : reject(); + } catch (e) { + reject(e); + } + }); + } + // === ЗАПУСК === + console.log(`🚀 Запуск сбора промо ссылок для ${QUERIES.length} запросов (макс. страниц на запрос: ${MAX_PAGES_PER_QUERY})...`); + (async function run() { + var totalLinks = 0; + for (let i = 0; i < QUERIES.length; i++) { + if (i > 0) await new Promise(r => setTimeout(r, QUERY_DELAY_MS)); + totalLinks += await processQuery(QUERIES[i]); + } + // === ФИНАЛЬНЫЙ ВЫВОД === + console.log('\n' + '='.repeat(50)); + console.log(`✅ СБОР ЗАВЕРШЁН`); + console.log(`📄 Запросов обработано: ${QUERIES.length}`); + console.log(`🔗 Промо ссылок собрано всего: ${totalLinks}`); + console.log('='.repeat(50) + '\n'); + if (totalLinks > 0) { + var outputText = allLinks.join('\n'); + console.log(outputText); + await copyToClipboard(outputText); + console.log('\n✅ Все промо ссылки (с разделителями по запросам) скопированы в буфер обмена!'); + console.log('💡 Нажмите Ctrl+V в любом месте для вставки'); + } else { + console.log('⚠️ Промо ссылки не найдены'); + } + })(); +})(); \ No newline at end of file