tradein/cian: обогащение новостроек не дало ни одной записи 8 суток подряд — 200 попыток, все прогоны done, причина в разборе а не в сети #2767

Closed
opened 2026-08-07 02:37:30 +00:00 by bot-backend · 7 comments
Collaborator

Найдено при разборе ночных прогонов. Задача обогащения новостроек Циана не обогатила ни одной записи как минимум восемь суток подряд, и каждый прогон при этом отчитывается успехом.

Замер

дата        статус  обработано  успешно  отказов
2026-08-07   done       25         0        25
2026-08-06   done       25         0        25
2026-08-05   done       25         0        25
2026-08-04   done       25         0        25
2026-08-03   done       25         0        25
2026-08-02   done       25         0        25
2026-08-01   done       25         0        25
2026-07-31   done       25         0        25

Восемь суток, 200 попыток, ноль результата, все прогоны done. Каждый тратит около семи минут.

Причина — разбор, а не сеть

BrowserFetcher: клиент создан, endpoint=http://tradein-browser:3000  proxy_lease_id=None
Cian newbuilding https://zhk-tihiy-centr-ekb-i.cian.ru: initialState extraction failed
newbuilding fetch returned None house_id=6667 (captcha / parse miss?)

Задача не берёт прокси и падает на извлечении состояния страницы. То есть страница получена, а разобрать её не удалось.

Два правдоподобных объяснения, и различить их я не могу без сохранённого ответа: либо Циан изменил разметку страниц новостроек, либо отдаёт защитную страницу. Живой запрос делать нельзя, поэтому нужен сохранённый образец — и его отсутствие само по себе часть проблемы.

Диагностика вводит в заблуждение

(captcha / parse miss?) — гипотеза автора кода, записанная так, что дальше читается как факт. Сегодня она подвела меня самого: я приписал этот провал нехватке прокси и написал об этом в #2638, после чего пришлось исправляться.

Это третий случай за сутки одного вида. У Яндекса было ABORT — 5 consecutive parse-None results (captcha wall?), и там «капча» тоже оказалась ни при чём: пять подряд нераспознаваемых ссылок в голове очереди.

Предлагаю относиться к таким строкам как к дефекту наблюдаемости: сообщение со знаком вопроса не должно попадать в мониторинг в виде утверждения. Либо причина установлена, либо сообщение говорит «причина не установлена», а не подсказывает ложную.

Что нужно

  1. Честный статус. 25 попыток и ноль результата — не done. Общий финализатор для этого уже есть (#2695, mark_backfill_finished), эта задача через него не проходит.
  2. Сохранять ответ при отказе разбора. Без образца причину не установить, а живой запрос запрещён. Одна страница на прогон — достаточно.
  3. Только потом — чинить разбор, когда станет ясно, что именно пришло.

Пункт 2 важнее пункта 3: сейчас каждая ночь уничтожает единственное свидетельство.

Связано: #2695, #2674, #2741.

Найдено при разборе ночных прогонов. Задача обогащения новостроек Циана **не обогатила ни одной записи как минимум восемь суток подряд**, и каждый прогон при этом отчитывается успехом. ## Замер ``` дата статус обработано успешно отказов 2026-08-07 done 25 0 25 2026-08-06 done 25 0 25 2026-08-05 done 25 0 25 2026-08-04 done 25 0 25 2026-08-03 done 25 0 25 2026-08-02 done 25 0 25 2026-08-01 done 25 0 25 2026-07-31 done 25 0 25 ``` Восемь суток, 200 попыток, **ноль результата**, все прогоны `done`. Каждый тратит около семи минут. ## Причина — разбор, а не сеть ``` BrowserFetcher: клиент создан, endpoint=http://tradein-browser:3000 proxy_lease_id=None Cian newbuilding https://zhk-tihiy-centr-ekb-i.cian.ru: initialState extraction failed newbuilding fetch returned None house_id=6667 (captcha / parse miss?) ``` Задача **не берёт прокси** и падает на извлечении состояния страницы. То есть страница получена, а разобрать её не удалось. Два правдоподобных объяснения, и различить их я не могу без сохранённого ответа: либо Циан изменил разметку страниц новостроек, либо отдаёт защитную страницу. **Живой запрос делать нельзя**, поэтому нужен сохранённый образец — и его отсутствие само по себе часть проблемы. ## Диагностика вводит в заблуждение `(captcha / parse miss?)` — гипотеза автора кода, записанная так, что дальше читается как факт. Сегодня она подвела меня самого: я приписал этот провал нехватке прокси и написал об этом в #2638, после чего пришлось исправляться. Это **третий случай за сутки** одного вида. У Яндекса было `ABORT — 5 consecutive parse-None results (captcha wall?)`, и там «капча» тоже оказалась ни при чём: пять подряд нераспознаваемых ссылок в голове очереди. Предлагаю относиться к таким строкам как к дефекту наблюдаемости: **сообщение со знаком вопроса не должно попадать в мониторинг в виде утверждения.** Либо причина установлена, либо сообщение говорит «причина не установлена», а не подсказывает ложную. ## Что нужно 1. **Честный статус.** 25 попыток и ноль результата — не `done`. Общий финализатор для этого уже есть (#2695, `mark_backfill_finished`), эта задача через него не проходит. 2. **Сохранять ответ при отказе разбора.** Без образца причину не установить, а живой запрос запрещён. Одна страница на прогон — достаточно. 3. Только потом — чинить разбор, когда станет ясно, что именно пришло. Пункт 2 важнее пункта 3: сейчас каждая ночь уничтожает единственное свидетельство. Связано: #2695, #2674, #2741.
Author
Collaborator

Уточнение: различитель уже известен, его просто не пишут

В самом коде разбора лежит документированная подпись (#972):

curl_cffi получает страницу без initialState под анти-ботом Cian.
tradein-browser верифицирован живым: 1.17 MB, initialState присутствует.

То есть страница без состояния = подпись антибота, и браузерный тракт заводился именно чтобы её обойти. Сейчас состояния нет и у браузерного — значит либо антибот достаёт и его, либо разметка изменилась.

Различить это можно по размеру ответа: 1.17 МБ против защитной заглушки — разница на порядок. Но размер в лог не пишется:

00:40:11  Cian newbuilding https://zhk-tihiy-centr-ekb-i.cian.ru: initialState extraction failed
00:40:29  … zhk-oazis-ekb-i.cian.ru: initialState extraction failed
00:40:47  … zhk-pihtovyy-ekb-i.cian.ru: initialState extraction failed

Ни байтов, ни кода ответа, ни признака заглушки. Время между попытками ровное (17-18 с), то есть браузер каждый раз реально грузит страницу — по таймингу тоже не различить.

Это удешевляет предложенный пункт 2

Я писал «сохранять ответ при отказе разбора». Достаточно меньшего: писать в лог размер полученной страницы. Одна строка, никакого хранения, и она сразу разделяет две гипотезы:

  • ~1 МБ — страница пришла целиком, значит изменилась разметка (наша задача, чинится разбором);
  • десятки килобайт — защитная заглушка, значит антибот достаёт браузерный тракт (внешнее, чинится не разбором).

Сохранение полного ответа остаётся полезным, но уже как второй шаг — когда станет ясно, какую именно страницу разбирать.

Почему это важнее, чем кажется

Восемь суток задача тратит по семь минут на прогон и пишет 25 предупреждений, ни одно из которых не содержит того единственного числа, которое ответило бы на вопрос. Логи теперь переживают деплой (#2741) — но переживает то, чего в них не записали.

Хранение логов и полнота логов — разные вещи, и первое без второго даёт только уверенность, что нужного не было.

## Уточнение: различитель уже известен, его просто не пишут В самом коде разбора лежит документированная подпись (#972): > `curl_cffi получает страницу без initialState под анти-ботом Cian.` > `tradein-browser верифицирован живым: 1.17 MB, initialState присутствует.` То есть **страница без состояния = подпись антибота**, и браузерный тракт заводился именно чтобы её обойти. Сейчас состояния нет и у браузерного — значит либо антибот достаёт и его, либо разметка изменилась. **Различить это можно по размеру ответа**: 1.17 МБ против защитной заглушки — разница на порядок. Но размер в лог не пишется: ``` 00:40:11 Cian newbuilding https://zhk-tihiy-centr-ekb-i.cian.ru: initialState extraction failed 00:40:29 … zhk-oazis-ekb-i.cian.ru: initialState extraction failed 00:40:47 … zhk-pihtovyy-ekb-i.cian.ru: initialState extraction failed ``` Ни байтов, ни кода ответа, ни признака заглушки. Время между попытками ровное (17-18 с), то есть браузер каждый раз реально грузит страницу — по таймингу тоже не различить. ## Это удешевляет предложенный пункт 2 Я писал «сохранять ответ при отказе разбора». Достаточно меньшего: **писать в лог размер полученной страницы**. Одна строка, никакого хранения, и она сразу разделяет две гипотезы: - **~1 МБ** — страница пришла целиком, значит изменилась разметка (наша задача, чинится разбором); - **десятки килобайт** — защитная заглушка, значит антибот достаёт браузерный тракт (внешнее, чинится не разбором). Сохранение полного ответа остаётся полезным, но уже как второй шаг — когда станет ясно, какую именно страницу разбирать. ## Почему это важнее, чем кажется Восемь суток задача тратит по семь минут на прогон и пишет 25 предупреждений, ни одно из которых не содержит того единственного числа, которое ответило бы на вопрос. Логи теперь переживают деплой (#2741) — но переживает то, чего в них не записали. **Хранение логов и полнота логов — разные вещи**, и первое без второго даёт только уверенность, что нужного не было.
Author
Collaborator

Проверил своё же «одна строка» — верно, и рядом нашлась дата

Утверждение «достаточно писать размер страницы» стоило проверить, прежде чем кто-то возьмётся. Место отказа:

async with BrowserFetcher(source="cian", endpoint=endpoint) as browser:
    html = await browser.fetch(zhk_url)

mfe = "newbuilding-card-desktop-frontend"
nb_state = extract_state(html, mfe=mfe, key="initialState")
if nb_state is None:
    logger.warning("Cian newbuilding %s: initialState extraction failed", zhk_url)
    return None

html в области видимости — значит len(html) это буквально один добавленный аргумент к уже существующей строке. Утверждение верно, работа тривиальна.

Дата, которая сужает окно

Прямо над местом отказа лежит комментарий:

# Cian ЖК-карточка: MFE 'newbuilding-card-desktop-frontend', key 'initialState'
# (verified live 2026-06-15).

То есть 15 июня разметка была именно такой и проверялась живьём. Отказы наблюдаются как минимум с 31 июля (раньше в счётчиках не смотрел).

Значит окно, в котором что-то изменилось, — между серединой июня и концом июля. Это не отвечает, что именно изменилось, но резко сокращает область поиска: если разметка, то правка Циана в этом промежутке; если защита, то её ужесточение тогда же.

Полезно и то, что комментарий содержит дату проверки. Без неё пришлось бы гадать, была ли разметка когда-нибудь такой вообще.

Уточнённая последовательность

  1. Добавить размер страницы в строку отказа — один аргумент, разделяет две гипотезы.
  2. По результату: либо чинить разбор (наша задача), либо признать, что защита достаёт браузерный тракт (внешнее, и тогда это меняет постановку — браузер заводился именно чтобы её обойти).
  3. Сохранение полного ответа — только если размера окажется недостаточно.
## Проверил своё же «одна строка» — верно, и рядом нашлась дата Утверждение «достаточно писать размер страницы» стоило проверить, прежде чем кто-то возьмётся. Место отказа: ```python async with BrowserFetcher(source="cian", endpoint=endpoint) as browser: html = await browser.fetch(zhk_url) mfe = "newbuilding-card-desktop-frontend" nb_state = extract_state(html, mfe=mfe, key="initialState") if nb_state is None: logger.warning("Cian newbuilding %s: initialState extraction failed", zhk_url) return None ``` `html` в области видимости — значит `len(html)` это буквально **один добавленный аргумент** к уже существующей строке. Утверждение верно, работа тривиальна. ## Дата, которая сужает окно Прямо над местом отказа лежит комментарий: > `# Cian ЖК-карточка: MFE 'newbuilding-card-desktop-frontend', key 'initialState'` > `# (verified live 2026-06-15).` То есть **15 июня разметка была именно такой и проверялась живьём**. Отказы наблюдаются как минимум с 31 июля (раньше в счётчиках не смотрел). Значит окно, в котором что-то изменилось, — **между серединой июня и концом июля**. Это не отвечает, что именно изменилось, но резко сокращает область поиска: если разметка, то правка Циана в этом промежутке; если защита, то её ужесточение тогда же. Полезно и то, что комментарий содержит дату проверки. Без неё пришлось бы гадать, была ли разметка когда-нибудь такой вообще. ## Уточнённая последовательность 1. Добавить размер страницы в строку отказа — **один аргумент**, разделяет две гипотезы. 2. По результату: либо чинить разбор (наша задача), либо признать, что защита достаёт браузерный тракт (внешнее, и тогда это меняет постановку — браузер заводился именно чтобы её обойти). 3. Сохранение полного ответа — только если размера окажется недостаточно.
Author
Collaborator

Working on this in PR #2768 — размер полученной страницы в строке отказа + честный статус через общий финализатор. Разбор намеренно не чинится.

Working on this in PR #2768 — размер полученной страницы в строке отказа + честный статус через общий финализатор. Разбор намеренно не чинится.
Author
Collaborator

ЖДЁТ ПРОГОНА — код доехал, производитель ещё не бежал. Не закрывать.

Что доехало (живой контейнер, не main)

tradein-scraper:
  packages/scraper-kit/src/scraper_kit/providers/cian/newbuilding.py:98
      return f"html_len={len(html)} antibot_markers={...}"
  packages/.../newbuilding.py:194
      "Cian newbuilding %s: initialState extraction failed — %s"
  app/tasks/newbuilding_enrich_backfill.py:765
      runs_mod.mark_backfill_finished(...)

Почему это ещё ничего не значит

Последний прогон newbuilding_enrich3337, 2026-08-07 00:39:58 → 00:47:18, то есть за семь с половиной часов ДО мержа PR #2768 (08:06). Он шёл на старом коде, и это видно в журнале дословно:

00:40:11 WARNING scraper_kit.providers.cian.newbuilding:
         Cian newbuilding https://zhk-tihiy-centr-ekb-i.cian.ru: initialState extraction failed
00:40:11 WARNING app.tasks.newbuilding_enrich_backfill:
         newbuilding fetch returned None house_id=6667 ... (captcha / parse miss?)

Ни html_len, ни диагностики — и та самая формулировка со знаком вопроса на месте. Статус прогона: done, processed 25 · succeeded 0 · failed_fetch 25. Девятые сутки подряд.

Дата ближайшего прогона — подтверждена самим планировщиком, не взята из головы

00:39:58 scheduler: claimed run_id=3337 source=newbuilding_enrich
                    next_run_at=2026-08-08T00:56:33+00:00

Записанная в брифе дата 08.08 00:56 UTC не устарела — она совпадает с тем, что планировщик уже положил в БД.

Критерий приёмки — записан ДО факта

Задача закрывается, когда по прогону newbuilding_enrich от 2026-08-08 выполнены оба условия:

  1. в журнале (journalctl -t tradein-scraper, теперь переживает деплой — #2741) строка отказа несёт html_len=<число>;
  2. scrape_runs.status этого прогона не done (при 25 попытках и нуле результата общий финализатор обязан дать failed или banned).

Одного первого мало: строка в логе без честного статуса оставит прогон невидимым для мониторинга, а это половина задачи.

Чего критерий НЕ решает

Само обогащение он не чинит и не должен: PR #2768 добавил различитель, а не разбор. По размеру страницы дальше разделяются две гипотезы — ~1 МБ (изменилась разметка, наша задача) против десятков килобайт (защита достаёт браузерный тракт, внешнее). Окно изменения сужено до промежутка между 2026-06-15 (дата живой проверки разметки в комментарии) и 2026-07-31.

Формулировку (captcha / parse miss?) в этом треде дальше не пересказываем как причину — это догадка автора кода, и она уже один раз ввела в заблуждение.

## ЖДЁТ ПРОГОНА — код доехал, производитель ещё не бежал. Не закрывать. ### Что доехало (живой контейнер, не main) ``` tradein-scraper: packages/scraper-kit/src/scraper_kit/providers/cian/newbuilding.py:98 return f"html_len={len(html)} antibot_markers={...}" packages/.../newbuilding.py:194 "Cian newbuilding %s: initialState extraction failed — %s" app/tasks/newbuilding_enrich_backfill.py:765 runs_mod.mark_backfill_finished(...) ``` ### Почему это ещё ничего не значит Последний прогон `newbuilding_enrich` — **3337, 2026-08-07 00:39:58 → 00:47:18**, то есть за семь с половиной часов ДО мержа PR #2768 (08:06). Он шёл на старом коде, и это видно в журнале дословно: ``` 00:40:11 WARNING scraper_kit.providers.cian.newbuilding: Cian newbuilding https://zhk-tihiy-centr-ekb-i.cian.ru: initialState extraction failed 00:40:11 WARNING app.tasks.newbuilding_enrich_backfill: newbuilding fetch returned None house_id=6667 ... (captcha / parse miss?) ``` Ни `html_len`, ни диагностики — и та самая формулировка со знаком вопроса на месте. Статус прогона: `done`, `processed 25 · succeeded 0 · failed_fetch 25`. Девятые сутки подряд. ### Дата ближайшего прогона — подтверждена самим планировщиком, не взята из головы ``` 00:39:58 scheduler: claimed run_id=3337 source=newbuilding_enrich next_run_at=2026-08-08T00:56:33+00:00 ``` Записанная в брифе дата **08.08 00:56 UTC** не устарела — она совпадает с тем, что планировщик уже положил в БД. ### Критерий приёмки — записан ДО факта Задача закрывается, когда по прогону `newbuilding_enrich` от 2026-08-08 выполнены **оба** условия: 1. в журнале (`journalctl -t tradein-scraper`, теперь переживает деплой — #2741) строка отказа несёт `html_len=<число>`; 2. `scrape_runs.status` этого прогона **не** `done` (при 25 попытках и нуле результата общий финализатор обязан дать `failed` или `banned`). Одного первого мало: строка в логе без честного статуса оставит прогон невидимым для мониторинга, а это половина задачи. ### Чего критерий НЕ решает Само обогащение он не чинит и не должен: PR #2768 добавил различитель, а не разбор. По размеру страницы дальше разделяются две гипотезы — ~1 МБ (изменилась разметка, наша задача) против десятков килобайт (защита достаёт браузерный тракт, внешнее). Окно изменения сужено до промежутка между 2026-06-15 (дата живой проверки разметки в комментарии) и 2026-07-31. Формулировку `(captcha / parse miss?)` в этом треде дальше не пересказываем как причину — это догадка автора кода, и она уже один раз ввела в заблуждение.
Author
Collaborator

Разобрано и проверено на проде: 0 из 25 → 25 из 25, график цен снова пишется

Прогон 3563 (2026-08-09 18:48, ручной смоук на живом коде):

attempted 25 · enriched 25 · failed 0 · price_dynamics_rows 64

Против девяти суток attempted 25 · enriched 0. В houses_price_dynamics 64 свежие строки по 10 домам, последняя запись до этого была 2026-07-26.

Что оказалось на странице

Гипотез было две, и обе оказались верны — но про разные страницы, и именно это девять суток мешало их различить.

374 168 байт, одинаково у всех ЖК — не оболочка и не карточка, а страница блокировки Циана: <title>Ошибка - Циан</title>, «Обнаружен подозрительный трафик», код страницы cian_waf_block, собственная аналитика помечает себя pageType:"VPNBlock". Одинаковый до байта размер объясняется тем, что различаются только IP и UUID запроса — обе строки фиксированной длины.

Почему диагностика молчала. Список маркеров не знал подписи cian_waf_block, а на этой странице нет ни «captcha», ни «вы не робот». antibot_markers=none читалось как вердикт «защиты нет» — хотя это было «список не знает такой защиты». Молчание сторожа снова оказалось неотличимо от его вердикта.

Что было сломано

Тракт, а не разбор. fetch_newbuilding — единственный cian-путь, строивший BrowserFetcher вручную, без proxy_provider; пропуск задокументирован в самой фабрике как #2160 P4. Сбор всегда выходил через один env-узел сайдкара — узел пула #1, чей exit-IP Циан забанил. Замер по узлам, одна и та же страница, один заход:

узел ответ
#1 residential 374 168 Б, cian_waf_block, состояния нет
#9/#10/#11 mobile 542-557 КБ, initialState разбирается текущим кодом

И разметка — но у другой части ЖК. Страницы, которые приходили целиком (810-993 КБ) и всё равно не разбирались, несут MFE newbuilding-card-desktop-fichering-frontend; старого имени на них нет вовсе. Ключ тот же (initialState), форма та же, и realtyValuation.data.priceDynamics там есть — это и есть причина пустой houses_price_dynamics.

PR

  • #2798 — подключение к пулу прокси + cian_waf_block в маркерах + бан узла на блоке
  • #2801регрессия #2798, поймана на проде через 17 с после деплоя: бан по одному слову «captcha» забанил два ЗДОРОВЫХ узла на страницах, которые разобрались. Признак сменил смысл при переносе: список создавался объяснять уже случившийся отказ разбора, а не служить детектором
  • #2804 — второе имя MFE + снятие ложного маркера «captcha» (любая здоровая карточка грузит скрипт SmartCaptcha)

Опровергнутые предпосылки

  • «Капчи и блокировки нет» — блокировка была, просто своя, не опознанная списком. В прогоне 08-08 половина ответов вдобавок были обычной стеной капчи (~15 КБ).
  • «Всем отдаётся одна оболочка» — одинаковый размер был у страницы блока, а не у оболочки; здоровые страницы разного размера приходили в том же прогоне.
  • «Данные уехали отдельным запросом» — нет: priceDynamics лежит в initialState, проверено на zhk-svetlyy (старый MFE) и zhk-kosmos (новый).
  • «Размер разделяет гипотезы» (моя же формулировка из #2768) — не разделяет: блок 374 КБ, здоровая карточка 542-993 КБ, диапазоны пересекаются. Различает только маркер.

Остаток

У части ЖК на старом фронте realtyValuation.data пуст — Циан считает динамику раз в месяц и не для всех. Это честная пустота, не дефект.

Баны узлов 9 и 10 (следствие регрессии #2801) истекают 23:47/23:48, до планового прогона в 00:18. Бан узла #1 до 00:21 — заслуженный, он и правда под WAF.

## Разобрано и проверено на проде: 0 из 25 → 25 из 25, график цен снова пишется Прогон 3563 (2026-08-09 18:48, ручной смоук на живом коде): ``` attempted 25 · enriched 25 · failed 0 · price_dynamics_rows 64 ``` Против девяти суток `attempted 25 · enriched 0`. В `houses_price_dynamics` 64 свежие строки по 10 домам, последняя запись до этого была **2026-07-26**. ## Что оказалось на странице Гипотез было две, и обе оказались верны — но про **разные** страницы, и именно это девять суток мешало их различить. **374 168 байт, одинаково у всех ЖК** — не оболочка и не карточка, а страница блокировки Циана: `<title>Ошибка - Циан</title>`, «Обнаружен подозрительный трафик», код страницы `cian_waf_block`, собственная аналитика помечает себя `pageType:"VPNBlock"`. Одинаковый до байта размер объясняется тем, что различаются только IP и UUID запроса — обе строки фиксированной длины. **Почему диагностика молчала.** Список маркеров не знал подписи `cian_waf_block`, а на этой странице нет ни «captcha», ни «вы не робот». `antibot_markers=none` читалось как вердикт «защиты нет» — хотя это было «список не знает такой защиты». Молчание сторожа снова оказалось неотличимо от его вердикта. ## Что было сломано **Тракт, а не разбор.** `fetch_newbuilding` — единственный cian-путь, строивший `BrowserFetcher` вручную, без `proxy_provider`; пропуск задокументирован в самой фабрике как #2160 P4. Сбор всегда выходил через один env-узел сайдкара — узел пула #1, чей exit-IP Циан забанил. Замер по узлам, одна и та же страница, один заход: | узел | ответ | |---|---| | #1 residential | 374 168 Б, `cian_waf_block`, состояния нет | | #9/#10/#11 mobile | 542-557 КБ, **initialState разбирается текущим кодом** | **И разметка — но у другой части ЖК.** Страницы, которые приходили целиком (810-993 КБ) и всё равно не разбирались, несут MFE `newbuilding-card-desktop-fichering-frontend`; старого имени на них нет вовсе. Ключ тот же (`initialState`), форма та же, и `realtyValuation.data.priceDynamics` там **есть** — это и есть причина пустой `houses_price_dynamics`. ## PR - #2798 — подключение к пулу прокси + `cian_waf_block` в маркерах + бан узла на блоке - #2801 — **регрессия #2798, поймана на проде через 17 с после деплоя**: бан по одному слову «captcha» забанил два ЗДОРОВЫХ узла на страницах, которые разобрались. Признак сменил смысл при переносе: список создавался объяснять уже случившийся отказ разбора, а не служить детектором - #2804 — второе имя MFE + снятие ложного маркера «captcha» (любая здоровая карточка грузит скрипт SmartCaptcha) ## Опровергнутые предпосылки - «Капчи и блокировки нет» — блокировка была, просто своя, не опознанная списком. В прогоне 08-08 половина ответов вдобавок были обычной стеной капчи (~15 КБ). - «Всем отдаётся одна оболочка» — одинаковый размер был у страницы блока, а не у оболочки; здоровые страницы разного размера приходили в том же прогоне. - «Данные уехали отдельным запросом» — нет: `priceDynamics` лежит в `initialState`, проверено на `zhk-svetlyy` (старый MFE) и `zhk-kosmos` (новый). - «Размер разделяет гипотезы» (моя же формулировка из #2768) — не разделяет: блок 374 КБ, здоровая карточка 542-993 КБ, диапазоны пересекаются. Различает только маркер. ## Остаток У части ЖК на старом фронте `realtyValuation.data` пуст — Циан считает динамику раз в месяц и не для всех. Это честная пустота, не дефект. Баны узлов 9 и 10 (следствие регрессии #2801) истекают 23:47/23:48, до планового прогона в 00:18. Бан узла #1 до 00:21 — заслуженный, он и правда под WAF.
Author
Collaborator

Поправка к моему выводу: блокировка БЫЛА, я прочитал диагностику неверно

Вчера я написал здесь и владельцу: «страница приходит целиком, antibot_markers=none — значит блокировки нет, Циан сменил разметку». Первая половина неверна.

Те самые 374 168 байт, одинаковые байт в байт у всех пяти ЖК, — это собственная страница блокировки Циана:

<title>Ошибка - Циан</title>
«Обнаружен подозрительный трафик»
Код страницы: cian_waf_block
pageType:"VPNBlock", extra:{antibot:true}

Одинаковый размер объясняется просто: различаются только IP и UUID запроса, обе строки фиксированной длины. 374 КБ набегают инлайновой аналитикой — то есть большой размер вообще не признак здоровой страницы.

Почему диагностика сказала «маркеров нет», когда блокировка была

Список маркеров создавался, чтобы объяснить уже случившийся отказ разбора, а не служить детектором. В нём не было слова cian_waf_block. Отсутствие известного маркера я прочитал как отсутствие блокировки — а это разные утверждения.

Тот же PR #2768, который дал различитель, дал и ложную уверенность: я поверил полю antibot_markers больше, чем оно заслуживало. Признак защиты годится как детектор только вместе с «состояния нет», сам по себе — нет.

Заодно опровергнута формулировка из #2768, что размер разделяет гипотезы: блок 374 КБ, здоровая карточка 542-993 КБ — диапазоны пересекаются, и в прогоне 08.08 половина ответов вдобавок была обычной стеной капчи (~15 КБ).

Обе гипотезы оказались верны — про разные страницы

Это и мешало их различить девять суток:

узел ответ
#1 residential (единственный, куда ходило обогащение) 374 168 Б, cian_waf_block, состояния нет
#9/#10/#11 mobile (пул) 542-557 КБ, initialState разбирается текущим кодом

А страницы, приходившие целиком и всё равно не разбиравшиеся, несут MFE под вторым именемnewbuilding-card-desktop-fichering-frontend. Старого имени на них нет вовсе; ключ и форма те же, и realtyValuation.data.priceDynamics там есть. Это причина пустой houses_price_dynamics с 26 июля.

То есть: и блокировка (не тому узлу), и смена имени MFE. Ни одна гипотеза по отдельности не объясняла всё.

Прод: 0 из 25 → 25 из 25

прогон attempted enriched failed строк динамики
3489 (09.08 00:36) 25 0 25 0
3559 (09.08 17:47) 25 15 10 0
3561 (09.08 18:13) 25 14 11 0
3563 (09.08 18:49) 25 25 0 64

houses_price_dynamics: 279 строк по 45 домам, самая свежая — 09.08 18:57, до этого таблица стояла с 26 июля.

Три PR: #2798 (подключение к пулу + маркер блока), #2801 (откат регрессии, пойманной на проде через 17 секунд после деплоя — бан по одному слову «captcha» забанил два здоровых узла: любая здоровая карточка грузит скрипт SmartCaptcha), #2804 (второе имя MFE + снятие ложного маркера).

Оставляю открытым до планового прогона 00:18 — сегодняшние 25/25 получены ручным триггером, и я хочу увидеть, что расписание отрабатывает само.

## Поправка к моему выводу: блокировка БЫЛА, я прочитал диагностику неверно Вчера я написал здесь и владельцу: «страница приходит целиком, `antibot_markers=none` — значит блокировки нет, Циан сменил разметку». **Первая половина неверна.** Те самые 374 168 байт, одинаковые байт в байт у всех пяти ЖК, — это **собственная страница блокировки Циана**: ``` <title>Ошибка - Циан</title> «Обнаружен подозрительный трафик» Код страницы: cian_waf_block pageType:"VPNBlock", extra:{antibot:true} ``` Одинаковый размер объясняется просто: различаются только IP и UUID запроса, обе строки фиксированной длины. 374 КБ набегают инлайновой аналитикой — то есть **большой размер вообще не признак здоровой страницы**. ### Почему диагностика сказала «маркеров нет», когда блокировка была Список маркеров создавался, чтобы **объяснить уже случившийся** отказ разбора, а не служить детектором. В нём не было слова `cian_waf_block`. Отсутствие известного маркера я прочитал как отсутствие блокировки — а это разные утверждения. Тот же PR #2768, который дал различитель, дал и ложную уверенность: я поверил полю `antibot_markers` больше, чем оно заслуживало. **Признак защиты годится как детектор только вместе с «состояния нет»**, сам по себе — нет. Заодно опровергнута формулировка из #2768, что размер разделяет гипотезы: блок 374 КБ, здоровая карточка 542-993 КБ — диапазоны пересекаются, и в прогоне 08.08 половина ответов вдобавок была обычной стеной капчи (~15 КБ). ### Обе гипотезы оказались верны — про разные страницы Это и мешало их различить девять суток: | узел | ответ | |---|---| | #1 residential (единственный, куда ходило обогащение) | 374 168 Б, `cian_waf_block`, состояния нет | | #9/#10/#11 mobile (пул) | 542-557 КБ, `initialState` разбирается **текущим** кодом | А страницы, приходившие целиком и всё равно не разбиравшиеся, несут MFE под **вторым именем** — `newbuilding-card-desktop-fichering-frontend`. Старого имени на них нет вовсе; ключ и форма те же, и `realtyValuation.data.priceDynamics` там есть. Это причина пустой `houses_price_dynamics` с 26 июля. То есть: **и блокировка (не тому узлу), и смена имени MFE.** Ни одна гипотеза по отдельности не объясняла всё. ## Прод: 0 из 25 → 25 из 25 | прогон | attempted | enriched | failed | строк динамики | |---|---|---|---|---| | 3489 (09.08 00:36) | 25 | **0** | 25 | 0 | | 3559 (09.08 17:47) | 25 | 15 | 10 | 0 | | 3561 (09.08 18:13) | 25 | 14 | 11 | 0 | | **3563 (09.08 18:49)** | 25 | **25** | **0** | **64** | `houses_price_dynamics`: 279 строк по 45 домам, самая свежая — **09.08 18:57**, до этого таблица стояла с 26 июля. Три PR: #2798 (подключение к пулу + маркер блока), #2801 (**откат регрессии, пойманной на проде через 17 секунд после деплоя** — бан по одному слову «captcha» забанил два здоровых узла: любая здоровая карточка грузит скрипт SmartCaptcha), #2804 (второе имя MFE + снятие ложного маркера). Оставляю открытым до планового прогона 00:18 — сегодняшние 25/25 получены ручным триггером, и я хочу увидеть, что расписание отрабатывает само.
Author
Collaborator

ЗАКРЫТО — плановый прогон отработал сам

Вчерашние 25 из 25 были получены ручным триггером, и я оставлял задачу открытой именно поэтому. Плановый прогон 3578, 10.08 00:28 UTC, без вмешательства:

{"failed": 0, "enriched": 25, "attempted": 25, "succeeded": 25,
 "failed_fetch": 0, "failed_resolve": 0, "duration_sec": 469}

Контраст с двумя предыдущими плановыми — теми, ради которых задача и заводилась:

прогон статус enriched failed
3411 (08.08 00:56) failed 0 25
3489 (09.08 00:36) failed 0 25
3578 (10.08 00:28) done 25 0

Отдельно: price_dynamics_rows: 0 — не отказ, но читается как отказ

Счётчик показал ноль, и я едва не прочитал это как «динамика цен снова не пишется». Проверил таблицу: в окне прогона (00:28–00:40) 64 строки с обновлённой отметкой, по 10 домам. Общее число строк при этом не изменилось — 279 до и после.

Причина в арифметике счётчика: price_dynamics_rows += max(0, pd_after - pd_before) — это прирост числа строк, а вставка идёт ON CONFLICT DO UPDATE по ключу (дом, месяц, источник, комнатность, тип цен, период). Вчера те же 64 точки были новыми — счётчик показал 64. Сегодня те же точки перезаписались — счётчик показал 0.

Арифметика честная, название вводит в заблуждение: «строк динамики цен: 0» читается как «ничего не записано», хотя записано 64. Завёл отдельно, потому что это ровно тема эпика — счётчик, отвечающий не на тот вопрос, который по нему задают.

График Циана за сутки не сдвинулся, поэтому новых точек и не должно было появиться. Это честная пустота, а не дефект.

Остаток, названный вслух

Динамика есть у 45 домов из 1874 циановских. Это не поломка обогащения: Циан считает её не для всех ЖК и обновляет редко. Но соотношение стоит помнить, прежде чем строить на этой таблице что-то видимое пользователю.

Закрываю.

## ЗАКРЫТО — плановый прогон отработал сам Вчерашние 25 из 25 были получены ручным триггером, и я оставлял задачу открытой именно поэтому. Плановый прогон **3578, 10.08 00:28 UTC, без вмешательства**: ``` {"failed": 0, "enriched": 25, "attempted": 25, "succeeded": 25, "failed_fetch": 0, "failed_resolve": 0, "duration_sec": 469} ``` Контраст с двумя предыдущими плановыми — теми, ради которых задача и заводилась: | прогон | статус | enriched | failed | |---|---|---|---| | 3411 (08.08 00:56) | failed | 0 | 25 | | 3489 (09.08 00:36) | failed | 0 | 25 | | **3578 (10.08 00:28)** | **done** | **25** | **0** | ## Отдельно: `price_dynamics_rows: 0` — не отказ, но читается как отказ Счётчик показал ноль, и я едва не прочитал это как «динамика цен снова не пишется». Проверил таблицу: в окне прогона (00:28–00:40) **64 строки с обновлённой отметкой**, по 10 домам. Общее число строк при этом не изменилось — 279 до и после. Причина в арифметике счётчика: `price_dynamics_rows += max(0, pd_after - pd_before)` — это **прирост числа строк**, а вставка идёт `ON CONFLICT DO UPDATE` по ключу (дом, месяц, источник, комнатность, тип цен, период). Вчера те же 64 точки были новыми — счётчик показал 64. Сегодня те же точки перезаписались — счётчик показал 0. Арифметика честная, **название вводит в заблуждение**: «строк динамики цен: 0» читается как «ничего не записано», хотя записано 64. Завёл отдельно, потому что это ровно тема эпика — счётчик, отвечающий не на тот вопрос, который по нему задают. График Циана за сутки не сдвинулся, поэтому новых точек и не должно было появиться. Это честная пустота, а не дефект. ## Остаток, названный вслух Динамика есть у **45 домов из 1874** циановских. Это не поломка обогащения: Циан считает её не для всех ЖК и обновляет редко. Но соотношение стоит помнить, прежде чем строить на этой таблице что-то видимое пользователю. Закрываю.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2767
No description provided.