feat(tradein): DomClick вторичка scraper via BrowserFetcher (#796) #904
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
вторичка
ИРД
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#904
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/796-domclick-scraper"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
DomClickScraper— парсит вторичку через headless BrowserFetcher (DataDome проходит, curl_cffi 401)fetch_city(city_id, rooms, pages)— citywide sweep с разбивкой по комнатностиa[href*="/card/"]→ source_id + title-parse (rooms/area/floor/price)settings.scraper_proxy_url(BrowserFetcher читает сам)domclick.ruдобавлен вscrape_allowed_hostsCloses / Related
Closes #796 (PoC steps 1-3 done; step 4 ЦИАН B2B — отдельный issue)
Test plan
uv run pytest tradein-mvp/backend/tests/scrapers/test_domclick.py -v— 37/37fetch_city(city_id=4, rooms=[2])с proxy на проде (city_id=4 = ЕКБ)parse_failurescounter после первого реального прогонаCode review — ⚠️ запрос изменений (1 блокер + 2 follow-up)
Хорошая реализация:
BrowserFetcherчерезasync with(нет утечки ресурсов), двухуровневая дедупликация (seen_idsв памяти +ON CONFLICTвsave_listings), proxy черезBrowserFetcher._proxy_dict(), нет hardcoded secrets/SQLi,request_delay_sec=8.0> Cian консервативно, ruff ≤100. 37 тестов на парсинг проходят.🔴 Блокер (нужно до merge) — source name mismatch
domclick.pyпишетsource="domclick", но весь остальной кодебейс использует"domklik":trade_in_pdf.py:182/192— PDF-экспортёр ищет"domklik"для цвета плашки + display-name «Домклик · Сбер»trade_in_pdf.py:803—_deal_source_keys = {"etazhi", "domklik", "rosreestr"}schemas/trade_in.py:51,446— docstring-enum'domklik'scrapers/base.py:66— comment'domklik'С
"domclick"в prod листинги ДомКлик не получат зелёную плашку «Домклик · Сбер» — экспортёр не найдёт source в маппинге (нейтральный цвет/имя вместо бренда).Fix: переименовать
source = "domklik"+ScrapedLot(source="domklik", ...)(domclick.py строки ~128, 134, 302, 308). Минимальный, upstream-consistent. + поправить соответствующий ассерт вtest_domclick.py.🟠 Follow-up (можно отдельным PR/issue, но желательно сразу)
1. Нет
get_scraper_delay()— задержка hardcoded. Все другие scrapers (avito/cian/n1/yandex) берутget_scraper_delay(self.name)в__init__из таблицыscraper_settings(runtime-configurable через admin API). DomClick хардкодит 8.0 → нельзя замедлить/ускорить без деплоя. Добавить вызов в__init__+"domklik": 8.0в_DEFAULT_DELAY_BY_SOURCE(scraper_settings.py).2. Нет детекции DataDome block-страницы — silent data loss.
BrowserFetcher.fetch()может вернуть HTTP 200 + DataDome block-HTML (протухший fingerprint / плохой proxy IP). Тогда_parse_html()вернёт[]→ sweep тихо завершится с нулём лотов,parse_failures=0— мониторинг не поймает блокировку. Другие scrapers это ловят явно (avito.pyAvitoBlockedError,yandex_realty.py_CAPTCHA_MARKERS/_is_captcha). Минимум — проверять DataDome-маркеры в начале_parse_htmlи логировать WARNING / raiseDomClickBlockedError.Заметка (не блокер)
admin.py:74sources: Literal["avito","cian","yandex","n1"]— DomClick там не зарегистрирован, т.е. через общий/admin/scrapeendpoint он пока недоступен (вызывается напрямую из Celery/admin). OK для этого PR, но стоит зафиксировать в issue, чтобы следующий не удивился.После фикса блокера #1 — повторный review + merge. 🟠-пункты можно вынести в follow-up issue, если не хочешь раздувать PR.
Manual review (code-reviewer subagent) — ⚠️ MINOR
Корректность/безопасность — без блокеров. Mergeable после 1 пункта ниже.
👍 Сильно
seen_idsвfetch_city+ DBON CONFLICT (dedup_hash)вsave_listings— верный паттерн.async with, нет утечек сессий); DataDome-детект через_is_blocked_page→ clean break.settings.scraper_proxy_urlчитается в__aenter__(не на импорте), не логируется.scrape_allowed_hostsрасширен корректно. SQL-инъекций нет (вся персистенция черезsave_listings). ruff line-100 ок (длинные строки — кириллица, счёт по символам).🔧 Перед merge (actionable)
domklikвscraper_settings. У всех остальных скраперов (avito/cian/n1/yandex/domrf) есть seeded-row через migr 053/054; без неё admin UI не покажет/не отредактирует delay для domklik (только ручной INSERT). Добавить055_scraper_settings_domklik.sql(или дописать в 054) сINSERT ... ON CONFLICT DO NOTHING.Post-merge polish (не блокеры)
domclick.py:224— dead-fallbacklot.source_id or lot.source_url(внутриfetch_citysource_id никогда None из-за guardsale__flat__); сделатьassertили убрать.domclick.py:261— добавить явныйexcept Exception: logger.error(...); breakпослеDomClickBlockedError, иначе ошибка_parse_htmlкрутит остаток страниц с 0 результатов._extract_price_from_children(прод-путь),_extract_address → None, малформед HTML,source_id=Nonedefensive-ветки.DomClickScraper.source = "domklik"— class-attr нигде не используется (другие скраперы определяют толькоname); убрать или задокументировать.Это ручной advisory-review (main-сессия). Финальный verdict + merge — за auto-code-reviewer.