Перейти к содержимому

Обход и технический аудит ​

Обход — это собственные доказательства инструмента. Он использует последовательный httpx-фетчер поверх стандартного HTML-парсера, соблюдает robots.txt, идёт по ссылкам того же origin в ширину, запускает все правила и сохраняет страницы и проблемы в SQLite, когда хранилище настроено.

См. также: Источники данных и квоты, Отчёты и диффы, Автоматизация и доставка, MCP и навыки агентов.

Запуск обхода ​

bash
lv24-seo crawl --start https://example.com/ --max-urls 500 --json

--start по умолчанию берётся из project.base_url. --max-urls по умолчанию равен [crawl] max_urls (500) и должен быть не меньше 1. У CLI нет отдельного верхнего лимита; MCP seo_crawl_site ограничивает запрос выше 5 000 URL и сообщает об этом предупреждением.

Обход последовательный и вежливый. concurrency в seo.toml зарезервирован для будущего пула воркеров и сегодня ни на что не влияет. Crawl-delay из robots.txt задаёт паузу между запросами.

Стартовый URL и каждый redirect-хоп проверяются на соответствие origin (схема, хост и порт) и robots.txt до отправки запроса. Чужой или запрещённый хоп никогда не запрашивается и сообщается как robots_blocked с заметкой о покрытии. Единственное исключение — сам robots.txt, его редиректы обрабатываются по RFC 9309, но всё равно под политикой SSRF и лимитом редиректов.

Лимиты и бюджеты ​

ЛимитЗначение
Тело обычной страницы5 МБ на страницу
Тело документа карты сайта50 МБ на документ
URL в карте сайтаПротокольный лимит 50 000
Документов карты сайта20 на обход
Глубина индекса карт сайта2
Редиректов на запрос5
Бюджет запросов1 + max_urls × (max_redirects + 1)
Сохраняемый текст страницы20 000 символов
Список проблем MCP500 проблем на ответ

Бюджет запросов отображается как summary.request_count и summary.request_budget. Когда он исчерпан, обход останавливается с заметкой, а не тратит запросы молча. Для 500 URL и 5 редиректов бюджет составляет 3 001 запрос.

Сохраняемый текст страницы ограничен 20 000 символов, но word_count вычисляется по полному видимому тексту и остаётся точным. Ограничение — это предел хранения, а не парсинга: извлечение ссылок, JSON-LD, hreflang и подсчёт слов по-прежнему используют полный документ. Ни одно текущее правило не читает сохранённый текст.

robots.txt и SSRF ​

robots.txt следует семантике RFC 9309 с самым длинным совпадением: побеждает наиболее конкретное правило, а при равенстве — Allow. Ответ 5xx или сетевая ошибка откладывают обход вместо fail-open. Отсутствующий robots.txt не считается ошибкой.

Адреса loopback, private и link-local отклоняются по умолчанию, а DNS повторно разрешается на каждом redirect-хопе, чтобы хост не мог перебиндиться в приватную сеть. Локальный аудит должен явно включить allow_private в фетчере; настройки по умолчанию, которая тихо разрешает приватные цели, нет.

Обнаружение карт сайта ​

Обнаружение начинается со строк Sitemap: в robots.txt плюс стандартного /sitemap.xml. Отсутствие стандартной карты сайта нормально и не считается ошибкой. Каждый кандидат, дочерний элемент индекса и редирект карты сайта должны оставаться на стартовом origin; чужой кандидат пропускается с заметкой.

Документы карты сайта получают бюджет тела 50 МБ и протокольный лимит 50 000 URL. Индекс карт может прочитать до 20 документов на глубину 2. URL, найденные только в карте сайта, добавляются во фронт обхода, поэтому обход может начаться с карты, даже если главная страница почти не ссылается на другие. Карта, которая не загрузилась, сообщает только тип исключения (HTTPError, TimeoutError, …), но не URL и не query-строку; URL с ?key=… не должен попадать в предупреждения или limitations[].

Покрытие карты сайта ​

crawl возвращает data.sitemap_coverage: что сайт заявляет, по сравнению с тем, что реально увидел обход. Шесть корзин:

КорзинаЗначение
unlinkedЕсть в карте сайта, получен успешно, но ни одна обойдённая страница на него не ссылается
missing_from_sitemapОбойдён, 200, индексируемый HTML, которого нет в карте сайта
not_okЕсть в карте сайта, но ответ не 200 или был редирект
noindexЕсть в карте сайта, но помечен noindex
blockedЕсть в карте сайта, но вне области обхода (robots или другой origin)
unvisitedЕсть в карте сайта, но не запрашивался; статус неизвестен, не «битый»

counts точны. Каждый список buckets ограничен 50 URL. complete ложен, а limitations[] называет корзины, которые являются лишь кандидатами, если обход или инвентарь карты сайта усечён, карта не загрузилась или часть URL осталась необойдённой. Если карты сайта нет вовсе, дифф пуст и говорит «пусто, а не чисто», вместо того чтобы помечать каждую страницу как отсутствующую.

Сетевая ошибка при получении попадает в unvisited, а не в blocked и не в not_ok: статус не наблюдался, поэтому состояние URL неизвестно.

Граф ссылок ​

data.graph — детерминированная сводка внутреннего графа ссылок:

  • nodes — страницы, успешно полученные со статусом 200.
  • edges — уникальные нормализованные внутренние пары источник-цель, без self-ссылок.
  • orphans — узлы без входящих ссылок и без пути от стартового URL.
  • broken — внутренние цели, которые были запрошены и ответили 4xx/5xx.
  • failed — внутренние цели, которые запрашивались, но не дали статус.
  • unvisited — внутренние цели, которые не запрашивались, например из-за исчерпания бюджета URL.

unvisited — неизвестно, а не «бито». Редирект, ведущий на здоровую страницу, является узлом, а не находкой. /a и /a/ остаются разными, пока запрос не докажет обратное, поэтому граф не сливает их до HTTP-проверки.

Правила ​

Правила — чистые функции контекста обхода: без сети, часов и файловой системы. Каждый модуль лежит в crawl/rules/rule_<slug>.py и экспортирует объект RULE; обнаружение автоматическое. Запускающий код проставляет id и серьёзность правила, поэтому ошибочное правило не может само себе приписать другой id или уровень.

СерьёзностьId правил
criticalnoindex-on-200, robots-blocked, server-error, sitemap-url-noindex
highbroken-internal-link, canonical-mismatch, client-error, hreflang-not-reciprocal, hreflang-target-not-ok, jsonld-event-required, jsonld-required, mixed-content, redirect-chain, sitemap-url-not-ok, soft-404, title-duplicate, title-missing
mediumcanonical-missing, h1-missing, hreflang-missing-x-default, html-lang-missing, html-lang-mismatch, image-alt-missing, meta-description-duplicate, meta-description-missing, thin-content
lowjsonld-organization-duplicate, h1-multiple, jsonld-organization, title-length

Две эвристики стоит знать:

  • thin-content помечает URL, похожие на статьи, с менее чем 200 словами, если только на странице не больше 20 внутренних ссылок и она не выглядит листингом.
  • soft-404 помечает страницу 200, в заголовке которой есть «not found», «404» или русское «не найдена», либо неглавную HTML-страницу с менее чем 30 словами.

Правила сообщают то, что наблюдал обход. Они не объявляют необойдённый URL битым и не заменяют ручную проверку бизнес-смысла.

Сохранённые страницы и проблемы ​

lv24-seo init создаёт <config dir>/data и пишет относительный data_dir, привязанный к конфигу. Когда каталог существует, crawl открывает data/seo.sqlite3 и сохраняет страницы и проблемы под id свойства обхода. Недельный отчёт читает последние сохранённые проблемы для своего раздела; обход без хранилища всё равно возвращает полный JSON, но ничего не сохраняет.

Без --json CLI печатает короткую человекочитаемую сводку. С --json конверт содержит summary, issues, graph, errors, blocked и sitemap_coverage. MCP seo_crawl_site фильтрует проблемы по include_rules, ограничивает возвращаемый список 500 и сообщает issues_shown, issues_truncated и issues_filtered_by в meta.

Source: _source/engine-data-sources.md, _source/engine-cli.md, _source/engine-releases-0.5.2.md.