Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 35 additions & 54 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,22 +105,16 @@ ruff check .
внутри `output_root`), а каждый файл сразу после скачивания
парсится/переносится в свой `<run_directory>` через `write_dataset`/
`finalize_raw`.
- Перед вводом новой фразы (`_collect_one`) снимается снапшот текста первой
строки таблицы; после `_set_phrase`/`_set_region` даётся короткое (3s,
`_wait_for(..., required=False)`) окно на то, чтобы он изменился. Это
**мягкая подсказка, не гейт**: две разные (например, близкие) фразы могут легально
дать одинаковую первую строку или обе — пустую таблицу, так что таймаут
здесь не бросает исключение и не роняет фразу — просто ничего не
подтверждает. `_set_phrase` сама проверяет URL (`words=`) и кнопку
скачивания, не содержимое таблицы; без этой подсказки риск тихо скачать
данные предыдущей фразы под именем следующей был бы выше, но полностью
он не устранён — гейт умышленно не строгий. Проверено на живом
авторизованном Chrome: близкая пара фраз («ремонт квартир» /
«ремонт квартир цена») собралась без ложного отказа за ~13с суммарно
(раньше строгий гейт на этой паре давал ~45с простоя и
`InterfaceChangedError`), и данные обеих фраз в `dynamics`/`regions`
корректно различаются — устаревания не произошло ни разу за несколько
прогонов.
- Перед вводом следующей фразы (`_collect_one`) используется снапшот текста
первой строки, снятый в предыдущем табличном виде **до** перехода на
финальную карту `regions`; карта строк не содержит. После `_set_phrase`/
`_set_region` даётся короткое (3s, `_wait_for(..., required=False)`) окно
на изменение текста. Это **мягкая подсказка, не гейт**: две разные
(например, близкие) фразы могут легально дать одинаковую первую строку
или обе — пустую таблицу, поэтому таймаут не бросает исключение и не
роняет фразу — просто ничего не подтверждает. `_set_phrase` сама
проверяет URL (`words=`) и кнопку скачивания, не содержимое таблицы.

- Порядок «скачать → распарсить → записать parquet → убрать raw» обязателен:
при любой ошибке в этом блоке (не только `CsvFormatError` — тот же
инвариант держится и для сбоя `write_dataset`/`finalize_raw`) скачанный
Expand Down Expand Up @@ -197,44 +191,31 @@ ruff check .
молча скачивает CSV только с заголовком. `regions` (карта) от этого
условия освобождён — у него в DOM вообще нет строк таблицы, и гейт по
ним завис бы навсегда.
- **Гейт по устаревшим строкам таблицы (issue #3).** Наличия строк
(`length > 0`) недостаточно: они могут быть строками *предыдущего* вида,
ещё не смытыми перерисовкой. Перед кликом по вкладке `_select_view`
проверяет, активна ли уже целевая вкладка (`_is_view_active`, `checked`
на её radio-input **до** клика). Если да — значит клик по ней ничего не
переключает (то же самое представление уже открыто), и требовать смены
содержимого таблицы было бы неверно: клик по уже активной вкладке
структурно не обязан её перерисовывать. Так происходит систематически
для самого первого представления каждой фразы: цикл видов в
`_collect_one` всегда начинается с `top_popular`, а Wordstat обычно уже
открывает именно эту вкладку сразу после поиска фразы — первая версия
этого гейта (безусловный снапшот + жёсткое требование смены) вешала
ровно это первое представление каждого прогона до таймаута (найдено на
втором раунде ревью, до попадания в прод). Только когда целевая вкладка
была неактивна до клика, `_select_view` снимает снапшот текста первой
строки таблицы (`_table_snapshot`, тот же приём, что и в `_collect_one`
для смены фразы) и требует, чтобы содержимое таблицы **отличалось** от
этого снапшота — в отличие от мягкой подсказки при смене фразы, здесь
это жёсткий гейт для настоящих переключений: таймаут тут — настоящий
`InterfaceChangedError`, а не молчаливое "не подтвердилось, но и не
страшно". `regions` (карта) освобождён и от этого условия, и от
проверки активности — по той же причине, что и от `length > 0`.
Проверено на живом Wordstat (CDP `9223`): одиночная фраза и batch из
двух фраз собираются без зависаний на первом представлении, `regions`/
`dynamics` корректно различаются между фразами.
- **Историческая проблема (issue #3), найдена ручным прогоном на живом
Wordstat**: CSV для `top_popular`/`top_related` (представления «Что
искали со словом…» и «Похожие запросы») иногда скачивался с одной
строкой — только заголовком, без данных, хотя `_select_view` дожидался
`TABLE_ROW_SELECTOR.length > 0` перед кликом по «Скачать». Причина —
`TABLE_ROW_SELECTOR` ловил наличие **каких-то** строк, не различая
старые (от предыдущего вида) и новые. Гейт по изменению содержимого
таблицы выше адресует эту причину для настоящих переключений вида;
`dtypes`/`row_count: 0` в манифесте по-прежнему честно отражают пустой
CSV, если он всё же случится (подтверждено живым прогоном — оба
табличных представления для нескольких проверенных фраз стабильно
приходят пустыми, без строк вообще, что похоже на отдельное,
самостоятельное поведение Wordstat для этих фраз, а не на гонку).
- **Готовность вида и содержимое таблицы (issue #3/#13).** Единственный
жёсткий гейт `_select_view` — активный `checked` radio-маркер, кнопка
скачивания и, только для `top_popular`/`top_related`/`dynamics`, хотя бы
одна строка `TABLE_ROW_SELECTOR`. Последнее не даёт нажать «Скачать» в
известном окне, когда radio уже сменился, а таблица ещё не отрисована и
CSV выходит только с заголовком. `regions` (карта) от строки освобождён:
в его DOM таблицы нет.

Сравнение текста таблицы до/после клика — полезная улика перерисовки, но
**никогда не жёсткий гейт**: разные вкладки законно могут иметь одинаковую
первую строку (или одинаково пустое содержимое). После жёсткого гейта
`_select_view` делает эту проверку отдельным
`_wait_for(..., required=False)`; таймаут лишь не подтверждает сигнал и
не отклоняет фразу. Поэтому `_is_view_active` больше не нужен: клик по
уже активной вкладке и обычное переключение используют один безопасный
гейт. Ретрай клика остаётся: две попытки делят общий timeout пополам.

- **Историческая проблема пустых CSV (issue #3).** На живом Wordstat
`top_popular`/`top_related` иногда скачивались только с заголовком, когда
DOM ещё показывал переходное состояние. Не ослабляйте обязательное
`TABLE_ROW_SELECTOR.length > 0` для табличных видов: при ручном прогоне
проверяйте, что `row_count` у всех четырёх экспортов не равен нулю.
Контентное сравнение не заменяет этот гейт и не должно снова стать
строгим.


- **`storage.py`** — файловая изоляция запусков: каждый вызов `collect`
создаёт новый неразрушающий каталог
Expand Down
Loading
Loading