Парсер robots.txt на PHP: как выбирается правило

0

robots.txt ♡

Одна лишняя строка в robots.txt может закрыть от поисковика весь сайт, и ты не заметишь этого неделями. Сидишь, ждёшь трафик, а поисковик тебе тихо ставит «прочитано» и уходит в игнор. Поэтому на портале есть проверка: она разбирает файл, ловит в нём ошибки и показывает, разрешён ли конкретный адрес для Googlebot и для Яндекса.

Как поисковик выбирает правило

Почти вся путаница вокруг robots.txt держится на двух правилах, о которых все вечно забывают.

Первое: из всех правил, подходящих к адресу, побеждает самое длинное. Не то, что выше в файле, и не то, что написано позже.

Второе: если длина одинаковая, побеждает Allow.

Благодаря этому у закрытой папки может быть открытый файл:

User-agent: *
Disallow: /private/
Allow: /private/press.html

Адрес /private/press.html разрешён, потому что правило Allow длиннее. А /private/other.html закрыт, и никакой мольбой его не открыть.

Как это выглядит в коде

В методе RobotsTxt::check() я перебираю правила выбранной группы и запоминаю победителя:

$winner = null;
foreach ($rules as $rule) {
    if (! self::matches($rule['path'], $path)) {
        continue;
    }
    $length = strlen($rule['path']);
    if ($winner === null || $length > strlen($winner['path'])
        || ($length === strlen($winner['path']) && $rule['type'] === 'allow')) {
        $winner = $rule;
    }
}

return ['allowed' => $winner === null || $winner['type'] === 'allow'];

Если ни одно правило не подошло, адрес разрешён: всё, что не запрещено, то разрешено. Философия «я не запрещал, значит, можно».

Звёздочка и знак доллара

Google и Яндекс понимают в путях два спецсимвола: * (любая последовательность знаков) и $ (конец адреса). Чтобы их поддержать, я превращаю шаблон в регулярное выражение:

$anchored = str_ends_with($pattern, '$');
$body = $anchored ? substr($pattern, 0, -1) : $pattern;
$regex = '#^'.str_replace('\*', '.*', preg_quote($body, '#')).($anchored ? '$' : '').'#';

Порядок важен. Сначала весь текст экранируется через preg_quote(), и только потом \* заменяется на .*. Сделаешь наоборот, и точки, скобки и знаки вопроса из путей начнут работать как спецсимволы регулярки. Классический кринж, который потом полдня отлавливаешь.

Какой робот читает какую группу

Файл делится на группы по строкам User-agent, и несколько таких строк подряд относятся к одной группе. Для конкретного робота берётся группа с самым длинным именем, которое входит в его название: например, googlebot сильнее звёздочки. Если своей группы у робота нет, ему достаётся группа со звёздочкой, для всех остальных.

Поэтому проверка на портале сразу выдаёт результат для трёх случаев: для всех роботов, для Googlebot и для Яндекса.

Какие косяки находит парсер

  1. В строке нет двоеточия, а значит, это вообще не директива.
  2. Директива User-agent без имени робота.
  3. Allow или Disallow стоит до первой строки User-agent, и роботы такое правило просто не учтут.
  4. Путь не начинается с / или *.
  5. Sitemap указан не полным адресом с https://.
  6. Неизвестная директива. К ней парсер подбирает похожую по расстоянию Левенштейна: вместо Dissalow вежливо предложит disallow.

Директивы-динозавры

Отдельно парсер ругается на Host и Crawl-delay. Яндекс давно забыл про Host, главное зеркало теперь задают редиректом. Google не учитывает Crawl-delay, а скорость обхода для Яндекса лучше выставлять в Вебмастере. То есть эти строки в файле просто пылятся.

Чего парсер не делает

Он учитывает BOM в начале файла и разные переносы строк. Вопрос «закрыт ли весь сайт» проверяется отдельно: нужен Disallow: / без перекрывающего Allow. Директиву Clean-param он разбирает только по синтаксису, а её действие не моделирует. Боты, кроме звёздочки, Googlebot и Яндекса, отдельно не проверяются.

Парсер показывает, как файл должен работать по правилам. Что робот делает на самом деле, смотри в Вебмастере и Search Console, там последнее слово за ними. Как я готовлю к поиску сам портал, включая его robots.txt, лежит в отдельной статье, а чужой файл я забираю через защищённый клиент.

Комментарии

Буду рада вашим мыслям, идеям и вопросам!
Давайте обсуждать ♡

Пока никто не написал.
Станьте первым! ♡