Compare commits

...

2 commits

Author SHA1 Message Date
0f7f5ea0ab fix(tradein/domclick-local): resume — убрать преждевременный added==0 break в _paginate
All checks were successful
CI / changes (pull_request) Successful in 14s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
При resume первые страницы каждого бакета = топ-qi офферы, уже лежащие в done →
_add_offers возвращал added=0 → _paginate делал break → enumerate собирал 0 новых
(st=0 при resume; на первом прогоне done пуст, не проявлялось). Терминаторы пагинации
— только пустая страница и offset>=bound. Подтверждено: resume со 114 done → st собрал
546 новых студий (660-114).
2026-06-27 22:39:47 +03:00
3cc015c249 fix(tradein/domclick-local): SERP enumerate — wait_for_selector вместо слепого render-wait
Карточки SERP появляются клиентской гидрацией через ~6-12с после domcontentloaded.
Фиксированный render-sleep ловил пустой DOM при медленном ответе → enumerate молча
собирал 0 ids (silent 0-coverage; всплыло при ускоренном профиле render 3-5).
Теперь fetch_serp_page ждёт 'a[href*=/card/]' явно (timeout 12с) → пустой ценовой
band корректно даёт 0 по таймауту, render_wait остаётся коротким settle. Подтверждено
enumerate-only --limit 5 с render 3-5.
2026-06-27 22:26:48 +03:00

View file

@ -770,7 +770,15 @@ async def fetch_serp_page(
DomClickBlockedError: 403 или QRATOR-маркеры в title.
"""
resp = await page.goto(url, wait_until="domcontentloaded", timeout=70000)
await asyncio.sleep(random.uniform(*render_wait)) # дать SSR отрендериться
# SERP рендерит карточки клиентской гидрацией через ~6-12с после domcontentloaded.
# Фиксированный sleep хрупок: при медленном ответе DOM ещё пуст → 0 ids (silent
# 0-coverage). Ждём появления card-ссылок ЯВНО; genuinely-пустой ценовой band →
# timeout → 0 offers (корректно). render_wait остаётся коротким settle поверх.
try:
await page.wait_for_selector("a[href*='/card/']", timeout=12000)
except Exception:
pass # пустой band или медленный рендер — читаем как есть (0 offers)
await asyncio.sleep(random.uniform(*render_wait)) # settle: дорисовать все 20 + title
status = resp.status if resp else 0
title = ""
try:
@ -844,10 +852,11 @@ async def _paginate(
)
if not offers:
break
added = _add_offers(offers, seen, done, out, target_new)
# NB: НЕ break при added==0. При resume первые страницы бакета — это топ-qi
# офферы, уже лежащие в `done` → added=0, но новые id есть на след. страницах.
# Терминаторы — только пустая страница (выше) и offset>=bound (total/cap).
_add_offers(offers, seen, done, out, target_new)
page_idx += 1
if added == 0:
break
async def _sweep_bucket(