Files
portal/app/scripts/render-yandex-list.cjs
T
Дмитрий c259855349 feat(автоподбор): рубрика из списка Яндекса в описание + межотраслевой промпт
Playwright тянет рубрику фирмы прямо из СПИСКА Яндекса («Ломбард, автоломбард»,
«Микрофинансовая организация», «Банк») без захода в карточку — быстро и бесплатно.
Рубрика идёт в описание конкурента → в похожесть-эмбеддинги (раньше меряли по
голому имени, хлам и целевое сбивались). Промпт анализатора обезличен: примеры
из РАЗНЫХ отраслей (стоматология/автосервис/доставка) + «подбери под описание
клиента» — движок универсален, не подточен под нишу займов Омеги. TDD 216/216, НЕ прод.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 07:00:41 +03:00

69 lines
3.0 KiB
JavaScript

// Usage: node render-yandex-list.cjs <url>
// Рендер страницы категории Яндекс.Карт локальным Playwright (Firecrawl её не берёт, §12.2).
// Скроллит КОНТЕЙНЕР СПИСКА результатов (подгрузка ленивая) и печатает JSON:
// { orgs: [{ name, id, href }] } — имя + id + ссылка на карточку организации.
// Сайт в списке Яндекса НЕ отдаётся (только на карточке) — здесь не собираем (шаг 1 = имя+карточка).
// require('playwright') резолвится из node_modules корня репо (скрипт лежит под app/scripts).
const { chromium } = require('playwright');
(async () => {
const url = process.argv[2];
let parsed;
try {
parsed = new URL(url);
} catch (e) {
console.error('bad url');
process.exit(2);
}
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
console.error('bad scheme');
process.exit(2);
}
const browser = await chromium.launch({ headless: true });
try {
const page = await browser.newPage({ locale: 'ru-RU' });
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 45000 });
await page.waitForTimeout(4000);
// Ленивая лента результатов — скроллим её контейнер, пока подгружается.
for (let i = 0; i < 14; i++) {
await page.evaluate(() => {
const el = document.querySelector('.scroll__container, .search-list-view__list, [class*="search-list"]');
if (el) el.scrollBy(0, 2000);
});
await page.waitForTimeout(800);
}
const orgs = await page.$$eval('a[href*="/maps/org/"]', (els) => {
const seen = new Set();
const out = [];
for (const e of els) {
const href = e.getAttribute('href') || '';
const m = href.match(/\/maps\/org\/[a-z0-9_-]+\/(\d+)/i);
if (!m || seen.has(m[1])) continue;
seen.add(m[1]);
const name = (e.getAttribute('aria-label') || e.textContent || '').trim();
if (!name) continue;
// Рубрика из списка (без захода в карточку): поднимаемся к контейнеру-сниппету, берём категорию.
let box = e;
for (let up = 0; up < 6; up++) {
if (box.parentElement) box = box.parentElement;
if (box.className && /snippet|search-business|card/i.test(box.className)) break;
}
const catEl = box.querySelector('[class*="category"], [class*="rubric"]');
const category = catEl ? catEl.textContent.trim().slice(0, 80) : '';
out.push({ name, id: m[1], href: href.split('?')[0], category });
}
return out;
});
process.stdout.write(JSON.stringify({ orgs }));
} finally {
await browser.close();
}
})().catch((e) => {
console.error(String(e));
process.exit(1);
});