157 lines
23 KiB
Markdown
157 lines
23 KiB
Markdown
# Источники данных RF4: исследование этапа 0
|
||
|
||
Дата первоначальной проверки: **2 сентября 2026 года**. Повторная проверка страницы и `robots.txt`: **3 сентября 2026 года**. Исследовалась только публичная веб-страница; игровой клиент, его трафик и закрытые протоколы не исследовались.
|
||
|
||
## Краткий вывод
|
||
|
||
Для первого адаптера следует получать серверный HTML страницы `https://rf4game.de/records/region/RU/`. На момент проверки таблица уже находится в исходном HTML и не требует JavaScript, авторизации или обхода защиты. Доступного JSON/XHR endpoint именно для рекордов не обнаружено.
|
||
|
||
Страница содержит общий WordPress AJAX-клиент с адресом `/wp-admin/admin-ajax.php`, но опубликованные на странице действия `gajax_Ratings` относятся к рейтингу игроков (`rating`, `rating_list`, `best`). Действия для таблицы рекордов не объявлены. Поэтому использовать или подбирать недокументированные AJAX-параметры не рекомендуется.
|
||
|
||
## Подтверждённые URL и параметры
|
||
|
||
- Абсолютные рекорды региона RU: `https://rf4game.de/records/region/RU/`.
|
||
- Недельная форма URL: `https://rf4game.de/records/weekly/region/RU/`.
|
||
- В навигации страницы перечислены регионы: `GL`, `RU`, `DE`, `US`, `FR`, `CN`, `PL`, `KR`, `JP`, `EN`.
|
||
- В навигации категорий видны: `records`, `ultralight`, `recordslight`, `bottomlight`, `sea`, `telestick`.
|
||
|
||
Категория и регион кодируются сегментами URL, а не query-параметрами. Фактический успешный ответ абсолютной страницы был `200`, `text/html; charset=UTF-8`, с `Cache-Control: no-cache`. Запрос `GET /robots.txt` вернул `404`, то есть явных инструкций для роботов на этом пути при проверке не было. Это не является разрешением на интенсивный сбор.
|
||
|
||
Серия быстрых проверок категорий позже получила одинаковые небольшие HTML-ответы вместо полных таблиц. Это может быть временной защитой или ограничением частоты; вывод требует повторной осторожной проверки. Именно поэтому исследовательский скрипт проверяет наличие таблицы и завершает работу с ошибкой, а не принимает произвольный HTML за пустой результат.
|
||
|
||
## Подтверждённый DOM-контракт
|
||
|
||
Корень таблицы: `div.records.flex_table`. Верхняя строка имеет шесть семантических классов в таком порядке:
|
||
|
||
1. `fish` — рыба;
|
||
2. `weight` — вес;
|
||
3. `location` — водоём;
|
||
4. `bait` — приманка/наживка (отображаемое значение находится в атрибуте `title` у `.bait_icon`);
|
||
5. `gamername` — игрок;
|
||
6. `data` — дата (именно `data`, не `date`).
|
||
|
||
Каждый `div.records_subtable` объединяет до нескольких результатов одной рыбы. Название рыбы находится только в заголовочной строке группы (`.fish .text`); вложенные строки имеют пустую ячейку рыбы и наследуют название группы.
|
||
|
||
Подтверждённые внутренние поля `OfficialRecord`:
|
||
|
||
| Поле | Источник/нормализация |
|
||
|---|---|
|
||
| `region` | сегмент URL, верхний регистр |
|
||
| `category` | сегмент URL/аргумент запуска |
|
||
| `fish` | `.fish .text` заголовка группы |
|
||
| `weight_g` | `.weight`; `kg`/`g` приводятся к целым граммам |
|
||
| `waterbody` | `.location` |
|
||
| `bait` | `.bait_icon[title]`, nullable |
|
||
| `player` | `.gamername`, nullable |
|
||
| `record_date` | `.data`, формат `D.MM.YY`/`DD.MM.YY` |
|
||
| `source_url` | URL полученной страницы |
|
||
|
||
## Изображения официального сайта
|
||
|
||
Проверка 12 сентября 2026 года не обнаружила стабильного публичного каталога вида `канонический slug → изображение` для рыб, водоёмов или снастей. Официальная [галерея водоёмов](https://rf4game.de/media/levels/) и категория [рыб](https://rf4game.de/media/fische/) содержат тематические публикации, а [руководство](https://rf4game.de/userguide/) — иллюстрации интерфейса, оснасток и карт. Это медиа-галереи, а не полный справочник с устойчивыми entity ID.
|
||
|
||
Повторная сверка локальной альфы показала 2 рыбы, 2 водоёма и 3 приманки. Первый userguide-manifest содержал 69 кандидатов: 67 общих reference и 2 изображения карт. После расширения сбора локальный audit на 13 сентября показывает 24 approved-ассета: 2 рыбы, 12 приманок/наживок и 10 общих reference. Наличие картинки в очереди всё равно не считается покрытием каталога; coverage появляется только после сохранения, визуальной проверки и назначения canonical `entity_key`.
|
||
|
||
Прямой индекс `/media/fische/` вернул только общие изображения оформления (`flags/de.png`, `banner_de.png`): содержимое галереи загружается динамически. Эти файлы остаются `reference`, а не ошибочно маркируются как рыбы. Исследование допустимого gallery endpoint должно использовать обычный публичный контракт сайта и тот же общий cooldown, без обхода защит.
|
||
|
||
Контрольная сверка 12–13 сентября 2026 года: RF4DB показывает 19 водоёмов и 252 вида рыб. Страница RF4MAP `/fishes` дала 228 уникальных кандидатов изображений рыб и 149 изображений приманок. Это означает минимум 24 отсутствующие рыбьи иконки; 149 нельзя считать числом всех снастей, поскольку в него не входят полные каталоги удилищ, катушек, лесок, крючков и прочих компонентов. Общие userguide-карты являются reference, а не картами конкретных сущностей, поэтому текущее waterbody-покрытие равно 0/19. Канонически подтверждены 2 из 252 fish-кандидатов и 12 tackle/bait-кандидатов; остальные entity-кандидаты остаются карантинными.
|
||
|
||
Страница рекордов визуально использует `.bait_icon`, но подтверждённый parser-контракт извлекает только `.bait_icon[title]`. Ни parser, ни `Fish`/`Waterbody`/`Bait` модели и API-схемы сейчас не сохраняют image URL. Даже если URL удастся извлечь из CSS или обновлённого DOM, он может быть presentation asset, меняться независимо от названия и покрывать только приманки, попавшие в рекорды.
|
||
|
||
До отдельного разрешения на медиапубликацию изображения официального сайта не скачиваются, не хотлинкаются и не отображаются как собственные. Разрешение использовать фактические данные не трактуется как разрешение републиковать графические произведения.
|
||
|
||
Fallback строится на собственных лёгких SVG: осмысленные семейства силуэтов рыб, типовые глифы снастей и детерминированные абстрактные отпечатки водоёмов. Текстовое название всегда остаётся основным идентификатором; абстрактный контур водоёма не называется картой, а глиф приманки — изображением конкретной модели.
|
||
|
||
С 12 сентября 2026 года разрешён локальный сбор медиаресурсов; 13 сентября размер одного окна увеличен до 40 assets на домен. `python -m rf4_research.media_cli URL` делает один учтённый HTML-запрос и добавляет кандидатов в `data/media/manifest.json`; `--download-one` сохраняет один asset, а `--download-batch --batch-limit 40` — до 40 на домен под одной резервацией окна. Следующий batch домена возможен через 30 минут. При 401/403/429 или сетевой ошибке домен останавливается сразу, при трёх последовательных invalid/missing — досрочно; каждый результат сохраняется в manifest. Перед записью проверяются raster-формат, совпадение MIME и предел 40 Мп. Статус `queued` или `stored` не означает соответствие сущности или разрешение на публичный вывод. Ручная команда с `--review-url`, `--decision`, `--entity-type` и `--entity-key` переводит сохранённый файл в `approved`; неподходящий материал явно получает `rejected`.
|
||
|
||
Скачанные исследовательские оригиналы из `data/media/files/` хранятся в Git вместе с manifest. Каждый путь должен быть представлен ровно одной записью с source URL, source page и SHA-256; `--audit` проверяет отсутствие пропавших, повреждённых и бесхозных файлов. Git-хранение обеспечивает воспроизводимость набора, но не меняет публикационный статус: на сайт разрешено выводить только вручную проверенные `approved`-ассеты. Пользовательские загрузки остаются отдельным контуром MinIO/S3.
|
||
|
||
`python -m rf4_research.media_cli --audit` не использует сеть: команда выводит сводку статусов и завершается с ошибкой при отсутствующем/изменённом файле, несовпадении SHA-256 или dimensions/MIME, некорректном approved-сопоставлении и наличии бесхозного файла.
|
||
|
||
`python -m rf4_research.media_cli --queue-plan` также не использует сеть. Он читает manifest и общий cooldown-state, группирует queued-ассеты по фактическому домену, показывает готовность и оставшееся время, состав очереди по типам и один следующий кандидат с приоритетом waterbody → fish → tackle → reference. Команда намеренно сообщает, что точные catalog gaps неизвестны, пока baseline содержит только количества без канонических имён.
|
||
|
||
14 сентября разрешённый русский каталог RF4DB вернул ровно 252 подписанных изображения рыб с `oss.rf4db.com`. После консервативной нормализации регистра, пробелов и `е/ё` с RF4MAP совпали 227 видов, ещё 25 закрывают остаток полного каталога. `--dedupe-queue` сохраняет provenance альтернативного источника, но переводит совпавшие URL в статус `duplicate`, чтобы не скачивать одинаковые сущности повторно. `rf4db.com`, `download.rf4db.com` и `oss.rf4db.com` используют единый ключ cooldown `rf4db.com`.
|
||
|
||
`python -m rf4_research.media_cli --coverage` также работает локально и сравнивает manifest с датированным `data/media/catalog-baseline.json`. Неизвестные контрольные значения хранятся как `null`: отчёт не выдаёт число найденных приманок за полноту всего каталога снастей.
|
||
|
||
Координат, проводки, времени поимки и устойчивого внешнего идентификатора записи в этой таблице нет. Локализация зависит от домена/языка страницы: проверенная `.de`-страница возвращает немецкие названия рыб, водоёмов и приманок даже для региона RU.
|
||
|
||
## Ручное получение snapshots водоёмов
|
||
|
||
Для ручного запуска используется [`scripts/fetch-waterbodies.py`](../scripts/fetch-waterbodies.py). Скрипт вызывает тот же guarded CLI, что и остальные исследовательские источники: общий `rf4db.com` cooldown резервируется до HTTP-запроса, поэтому повтор после `403` или сетевой ошибки не выполняется раньше разрешённого окна.
|
||
|
||
Для страницы, которая требует обычного браузерного JavaScript, используется Chromium-launcher с отдельным persistent profile:
|
||
|
||
```bash
|
||
node scripts/fetch-waterbodies-chromium.mjs catalog
|
||
node scripts/fetch-waterbodies-chromium.mjs detail \
|
||
--url https://download.rf4db.com/ru/maps/level_019_american_pond
|
||
```
|
||
|
||
Он сначала резервирует тот же cooldown, затем открывает Chromium, сохраняет DOM и передаёт его локальному parser-у. Первый запуск можно оставить headed и вручную завершить обычную проверку сайта; `--headless` предназначен только для страниц, которые уже открываются без пользовательского действия. CAPTCHA и защитные challenge не автоматизируются.
|
||
|
||
```bash
|
||
./scripts/fetch-waterbodies.py catalog
|
||
./scripts/fetch-waterbodies.py detail \
|
||
--url https://download.rf4db.com/ru/maps/level_019_american_pond
|
||
```
|
||
|
||
Результат сохраняется атомарно в `.cache/waterbodies/` с timestamp-именем; `403`, неполный ответ и cooldown оставляют прежние snapshots нетронутыми. Если RF4DB показывает защитную страницу в разрешённой Chromium-сессии, сохраните полученный HTML и разберите его без повторного HTTP:
|
||
|
||
```bash
|
||
./scripts/fetch-waterbodies.py catalog --html /path/to/maps.html
|
||
./scripts/fetch-waterbodies.py detail \
|
||
--url https://download.rf4db.com/ru/maps/level_019_american_pond \
|
||
--html /path/to/detail.html
|
||
```
|
||
|
||
`--html` не резервирует cooldown и не обходит защиту: challenge/неполный документ будет отклонён строгим parser-ом. Скрипт намеренно не импортирует данные в PostgreSQL автоматически. После ручной проверки snapshot импортируется отдельно через `python -m app.cli import-waterbody-catalog` или `import-waterbody-detail`; для запуска из Docker файл передаётся в контейнер через stdin.
|
||
|
||
## Рекомендуемый режим получения
|
||
|
||
- Один понятный `User-Agent`, таймаут 20 секунд.
|
||
- Начать не чаще одного раза в 60 минут на выбранную комбинацию категории и региона.
|
||
- Не запускать параллельный обход всех регионов/категорий.
|
||
- Добавить кэш, ограниченные повторы с экспоненциальной задержкой и общий лимит запросов перед продуктивным импортом.
|
||
- При исчезновении таблицы, изменении порядка классов или ошибке HTTP считать запуск неуспешным и сохранять прежние данные.
|
||
- Повторно проверить условия использования и связаться с владельцем сайта до регулярного производственного сбора; отсутствие `robots.txt` не заменяет разрешения.
|
||
|
||
При повторной проверке 3 сентября 2026 года страница рекордов по-прежнему публично отдавала таблицу, а `https://rf4game.de/robots.txt` снова вернул `404`. Явного разрешения на автоматический сбор это не даёт. Поэтому планировщик реализован как opt-in профиль Compose `scheduler`, не запускается обычной командой `docker compose up` и не должен включаться во внешнем окружении до проверки условий использования или согласования с владельцем сайта.
|
||
|
||
## Сравнение с `hurfy/rf4-api`
|
||
|
||
Репозиторий `hurfy/rf4-api` создан в августе 2024 года; последний push, видимый через GitHub API на дату исследования, был 14 января 2025 года. README прямо называет проект находящимся в разработке. Он использует Django, Celery и браузерный WebDriver, перебирает регионы и категории, затем разбирает те же классы `records_wrapper`, `records_subtable`, `gamername`, `weight`, `location`, `bait_icon`, `data`.
|
||
|
||
Полезные архитектурные идеи:
|
||
|
||
- отделить построение URL, получение HTML, разбор и сохранение;
|
||
- передавать регион и категорию вместе с сырой страницей;
|
||
- нормализовать вес и текст до записи в БД.
|
||
|
||
Что нельзя переносить без проверки:
|
||
|
||
- список категорий проекта ограничен `records`, `ultralight`, `telestick` и уже не отражает всю текущую навигацию;
|
||
- WebDriver избыточен для подтверждённой серверной HTML-страницы;
|
||
- парсер опирается на позиционный поиск `.rows` и не проверяет контракт колонок;
|
||
- заявленные в README охват и расписание сами по себе не доказывают текущую работоспособность.
|
||
|
||
## Фикстура и исследовательский код
|
||
|
||
`tests/fixtures/records_ru_sample.html` — сокращённая обезличенная фикстура, сохраняющая подтверждённую вложенность и классы. Реальные ники и реальные сочетания уловов в неё не переносились. `rf4_research/records.py` получает ровно одну переданную страницу, проверяет контракт колонок и выдаёт список типизированных `OfficialRecord`.
|
||
|
||
Unit-тест покрывает заголовочную и вложенную записи, килограммы/граммы, большой вес с разделителем тысяч и отказ при изменении колонок.
|
||
|
||
## Что остаётся предположением
|
||
|
||
- Все перечисленные категории и регионы стабильно отдают таблицу при умеренной частоте запросов.
|
||
- Формат двухзначного года сохранится; сейчас он интерпретируется стандартным правилом Python как 2000-е для наблюдаемых значений.
|
||
- Сайт разрешит регулярный производственный опрос раз в час.
|
||
- DOM-классы останутся стабильнее локализованных заголовков.
|
||
- Отсутствие найденного JSON endpoint не доказывает, что внутреннего endpoint вообще нет; подтверждено лишь, что для публичного сценария он не нужен и на странице не объявлен.
|
||
|
||
## Источники
|
||
|
||
- Официальная страница: <https://rf4game.de/records/region/RU/>
|
||
- Исследованный сторонний проект: <https://github.com/hurfy/rf4-api>
|
||
- Метаданные репозитория GitHub API: <https://api.github.com/repos/hurfy/rf4-api>
|