Files
portal/app/scripts/render-page.cjs
T
Дмитрий aca52d130d fix(автоподбор): рендер сайта больше не теряет номера при таймауте Playwright
Шаг 2: CurlPlaywrightFetcher.site() curl'ом успешно тянул страницу с tel:-номерами,
но render() (node render-page.cjs) висел на waitUntil networkidle и падал по таймауту.
Symfony Process::run() при таймауте бросает ProcessTimedOutException — оно вылетало из
site(), SiteOpener обнулял весь сбор. Итог: 0 номеров с сайта (боевой кейс Центрофинанс,
tenant info@lkomega.ru, прогон 07.07 14:07, только сайт+карточка, ни одного code-номера).

- render()/renderFirm() ловят \Throwable -> null: падение/вис рендера НЕ фатально,
  curl-HTML с номерами 'в коде сайта' сохраняется
- render-page.cjs: networkidle -> domcontentloaded + пауза; рендер 30с+вис -> 3.7с (боевая проверка)
- сиды curlRunner/nodeRunner для офлайн-теста; регресс-тест воспроизводит потерю номеров

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 18:16:42 +03:00

41 lines
1.9 KiB
JavaScript

// Usage: node render-page.cjs <url>
// Печатает JSON: { html, visiblePhones: [...] } — отрендеренный DOM и видимые tel:-номера.
const { chromium } = require('playwright');
(async () => {
const url = process.argv[2];
// Защита от SSRF/локального доступа: только http/https.
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();
// 'domcontentloaded' вместо 'networkidle': «шумные» сайты (аналитика, чат-виджеты, коллтрекинг)
// никогда не «затихают» — networkidle упирался в 30с и рендер падал по таймауту, обнуляя весь сбор
// номеров с сайта. Берём DOM рано, затем короткая пауза, чтобы скрипты коллтрекинга
// (calltouch/comagic/…) успели подставить свой номер в видимые tel:-ссылки.
try {
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 12000 });
} catch (e) {
// даже если навигация не уложилась в таймаут — забираем то, что успело отрендериться
}
await page.waitForTimeout(2500);
const html = await page.content();
const visiblePhones = await page.$$eval('a[href^="tel:"]', els =>
els.map(e => (e.textContent || '').trim()).filter(Boolean)
);
process.stdout.write(JSON.stringify({ html, visiblePhones }));
} finally {
await browser.close();
}
})().catch(e => { console.error(String(e)); process.exit(1); });