bot_recognizer: Детектор веб-ботов

Не секрет, что в инете сейчас трудятся тысячи ботов, периодически читающих содержимое сайтов, индексирующих, пытающихся постить сообщения (со спамом), собирающих адреса email (email harvesting) и т.д.

Данный класс позволяет автоматизировать процесс идентификации бота и выполнения дальнейших действий (генерации контента) в зависимости от результатов идентификации.

Основная идея не нова - проверка по базе IP адреса и/или строки user-agent. (Конечно, этот метод не позволяет обнаружить боты, использующие спуфинг IP адреса и строки агента.)

Описания ботов могут браться как из текстового файла (строки, разделенные спец-символом), так и из SQL базы.

В случае использования SQL как хранилища описаний ботов, возможно как использование дополнительного класса-обертки доступа к MySQL (включенного в дистрибутив), так и классов из Zend Framework (используется объект одного из классов Zend_Db_Adapter_* ).

Распознавание бота может выполняться совместной проверкой IP адреса и строки user-agent, или только одного из параметров.

В настоящий момент класс имеет следующий функционал:

Установка на сайт

Простые примеры использования

# Используем Dispath() для авто-срабатывания на бота
require_once('bot_recognizer.php');

$botrec = new CBotRecognizer();
$botrec->RegisterActionForBots('MicrosoftBots','msn');
$botrec->RegisterActionForBots('GoogleBots','google');
$botrec->Dispatch();

echo "Hello, dear Human !"; // отображается если ботов не было детектировано
#...

function MicrosoftBots() {
  die("This text is shown for Microsoft spiders (msn) !");
}
function GoogleBots() {
  die("This text is shown for Google bots");
}
# Используем GetBotId() и обрабатываем ИД обнаруженного бота сами:
require_once('bot_recognizer.php');

$botrec = new CBotRecognizer();
$botid = $botrec->GetBotId();

if($botrec->IsMaliciousBot()) die("You are a malicious bot. Get lost !");

switch($botid) {
  case 'goolge' : case 'msn' :
      echo "This is Microsoft or Google bot. Let them in...";
      break;
  case CBotRecognizer::UNDEFINED_BOT:
      die("some undefined bot detected !");
      break;
  case 0:
     echo "Hello, dear Human !"; // this text is shown only if none of MicrosoftBots and GoogleBots called.
     break;
  default: # all other detected bots...
     echo "Bot $botid detected, no particular action for it !";
     break;
}

Использование класса CBotRecognizer

Простейший пример показан выше, все параметры берутся "по умолчанию" : описания ботов берутся из текстового файла "bot-defs.txt", который должен лежать в одной папке с модулем класса, метод поиска - по IP и строке user-agent. Используемый файл описаний ботов "bot-defs.txt" имеет простой текстовый формат, одна строка - одно описание, поля разделяются символом "|". Каждая строка должна содержать 4-5 полей в следующем порядке:
  1. короткое имя (ИД) бота (оно будет возвращено методом GetBotId(), будем называть его ИД бота
  2. начальный IP адрес,
  3. конечный IP адрес,
  4. подстрока, идентифицирующая данный бот в случае обнаружения в user-agent
  5. необязательный параметр (bot type) - тип бота
  6. необязательный параметр (malicious) - признак "зловредности" данного бота

Фрагмент файла bot-defs.txt
msn|65.55.211.113|65.55.211.119|msnbot
msn|65.55.232.22|65.55.232.22|msnbot
google|66.249.71.22|66.249.71.139|Googlebot

alexa|67.202.54.191|67.202.54.191|ia_archiver
yahoo|72.30.142.240|72.30.142.240|Yahoo!
webalta|76.73.62.242|76.73.62.242|webalta crawler

# some malicious bots from kloth.net, "1" in 5-th field means "malicious bot"
rdprm.gouv.qc.ca|207.96.148.8|207.96.148.8||1
easydl|76.10.155.74|76.10.155.74|EasyDL|1
Более одной строки могут иметь один и тот же ИД бота, т.к. боты одной компании могут работать на многих IP адресах и иметь разные строки user agent. Чтобы убедиться в этом, достаточно посмотреть любой из файлов списков ботов на iplists.com.

В случае использования режима "DB storage" возможны два варианта работы с БД :

1. Через объект класса CDbEngine из прилагающегося модуля as_dbutils.php :
require_once('as_dbutils.php'); // database access wrapper
require_once('bot_recognizer.php');

$mydb = new CDbEngine(DBTYPE_MYSQL,'localhost','user','password','mydatabase');
$botrec = new CBotRecognizer(array('dbobject'=>$mydb));
#...
$botrec->Dispatch();

2. Если сайт написан на Zend Framework (ZF), могут применяться Zend-овские компоненты работы с БД : (в наших примерах используется компонент MySQL PDO)
require_once('Zend/Db.php');
require_once('Zend/Db/Table.php');
require_once('Zend/Db/Adapter/Pdo/Mysql.php');

require_once('bot_recognizer.php');

# create Zend db adapter object...
$mydb = new Zend_Db_Adapter_Pdo_Mysql(
  array( 'host'=> 'localhost',
         'username' => 'user',
         'password' => 'password',
         'dbname'   => 'mydatabase'));

Zend_Db_Table::setDefaultAdapter($dbAdapter);

# the rest is identical:
$botrec = new CBotRecognizer(array('dbobject'=>$mydb));
#...
$botrec->Dispatch();
Перед использованием режима "хранилища БД" , надо создать таблицу в БД и загрузить в нее начальное состояние списка описаний. Создать таблицу можно двумя способами :
  1. Вызовом метода CBotRecognizer::CreateBotDefTable(); в этом случае создается пустая таблица [prefix]bot_definitions.
    (Внимание: если таблица с таким же именем уже была в базе, она будет стерта, так что проверьте ваши таблицы на несовпадение имени, и используйте подходящий префикс имени таблицы !)
  2. Вызовом CBotRecognizer::LoadBotDefinitionsFile('',true) : этот метод (пере)создает чистую таблицу (вызвав CreateBotDefTable()) и грузит в нее начальные описания из файла bot-defs.txt . Возможно, перед этим вы захотите добавить в файл свои описания ботов, чтобы они сразу попали в базу.

Список методов класса



CBotRecognizer([$param]) - конструктор класса.

Переданный $param должен быть ассоциативным массивом значений, со следующими ключами :
ключназначение
tableprefix Задает префикс имени таблицы для описаний ботов. По умолчанию префикс botrec_, поэтому стандартное имя таблицы - "botrec_bot_definitions".
dbobject Передает объект для работы с БД, который должен быть заранее создан и подготовлен для вызова SQL запросов. Это либо объект одного из классов Zend_Db_Adapter либо объект класса CDbEngine object (см. as_dbutils.php). Параметры доступа к базе должны быть установлены заранее (см.примеры выше).
По умолчанию объект CBotRecognizer создается в режиме "файловое хранилище". Т.е. если элемент "dbobject" в переданном массиве отсутствует, конструктор пытается загрузить список описаний из bot-defs.txt (или другого файла, если передан параметр sourcefile').
Передавая параметр dbobject, вы активируете режим "хранилища в БД", так что дальнейший поиск ботов будет происходить в БД.
searchmode Одно из значений :
CBotRecognizer::SEARCH_IP_ONLY,
CBotRecognizer::SEARCH_IP_OR_AGENT
или CBotRecognizer::SEARCH_AGENT_ONLY.

По умолчанию режим поиска - SEARCH_IP_OR_AGENT (equal 1), при котором поиск бота ведется как по IP адресу, так и по строке user-agent, и если любое условие выполнится, ИД найденного бота будет возвращен как результат поиска. Другие значения включают режим поиска только по одному из признаков.
sourcefile Передает имя файла "начальных" значений описаний ботов. Перебивает стандартное "bot-defs.txt"
worktime задает "рабочее время" для функции диспетчирования ботов (Dispatch()). Если текущее время окажется вне заданного интервала, метод Dispatch() не выполнит своей работы и сразу вернет управление. Значение параметра - строка в виде "HH:MI-HH:MI" или массив : array($time_from, $time_to). Время должно быть указано с ведущими нулями.
Например, если задать интервал "05:00-07:00", Dispatch() будет отрабатывать только с 5 или до 7-ми часов утра.


CreateBotDefTable() - создает пустую таблицу в БД для описаний ботов. Имя таблицы [passed_prefix]bot_definitions. (Префикс в имени по умолчанию - "botrec_")

EmulateBot($ip [, $agent]) - задает режим эмуляции бота.
Используется для отладки поведения сайта при запросе от данного бота. Последующие вызовы GetBotID() и Dispatch() будут такие же, ка и при запросе к сайту от данного бота (клиента с данным адресом и строкой uaser-agent).

SetSearchMode($mode) - задает метод определения бота ($mode - одно из значений CBotRecognizer::SEARCH_IP_ONLY (0), CBotRecognizer::SEARCH_IP_OR_AGENT (1) или CBotRecognizer::SEARCH_AGENT_ONLY (2).

GetBotId($ua='',$ip='') - ищет в базе и возвращает ИД бота в соответствии с IP адресом и/или user-agent. Если точного соответствия не найдено, но в строке user-agent обнаружено одно из слов-признаков бота (spider, crawl и т.д.), возвращается значение CBotRecognizer::UNDEFINED_BOT (-1). Если найденный бот признан "зловредным" (malicious), включается соответствующий флаг, и впоследствии метод Dispath() вызовет функцию, назначенную для всех malicious - ботов.
Необязательные параметры $ua и $ip='' могут использоваться, чтобы определить бота для нужной строки user-agent и/или IP адреса, вместо адреса и строки клиента. Это может быть полезно, например, при анализе логов доступа к сайту.
Возвращаемое значение: ИД бота (строка) или CBotRecognizer::UNDEFINED_BOT (-1) или false если бот не детектирован.

LoadBotDefinitionsFile($srcfile='', $clearexisting=false) - грузит описания из "стандартного" файла описаний, добавляя записи к уже существующим (если есть). Если активен режим "файлового хранилища", описания добавляются во внутренний массив в объекте. В противном случае записи заносятся в таблицу БД.
Если параметр $srcfile пуст или не передан, загрузка идет из bot-defs.txt.
Необязательный второй параметр $clearexisting задает очистку (и пере-создание таблицы в БД) существующей баз описаний, либо очистку внутреннего массива описаний (при работе в файл-режиме).

ImportBotsFromUrl($bot_id, $url,$file_type=0,$malicious=0) служит для импорта описаний ботов из внешних источников - напрямую из файлов в интернете или из сохраненых локально. Файлы должны быть одного из поддерживаемых форматов, на данный момент поддерживается только формат текстовых файлов с сайта iplists.com :
# Фрагмент файла из google.txt file, загруженного с www.iplists.com:
# UA "Mediapartners-Google/2.1"
# UA "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
# UA "gsa-crawler (Enterprise; S4-E9LJ2B82FJJAA; me@mycompany.com)"
209.185.108
209.185.253
209.85.238
209.85.238.11
209.85.238.4
...
Как видно во фрагменте, некоторые строки содержат строку user-agent, некоторые - отдельные IP адреса или подсети.

ImportBotsFromUrl() разбирает содержимое файла, собирая все строки user-agent и IP адресов диапазоны базы, и загружает их в БД либо в память (в зависимости от активного режима хранения). IP адреса под-сетей типа 209.85.238 конвертируются в соответствующие диапазоны (209.85.238.0 - 209.85.238.255).
Отметим, что для оптимизации базы и ускорения поиска перекрывающиеся, вложенные или примыкающие диапазоны адресов объединяются. Так, набор диапазонов 64.68.80, 64.68.81, 64.68.82 сольется в один диапазон IP адресов 64.68.80.0 - 64.68.82.255.

AddBotDefinition($botid, $ipfrom, $ipto=0, $useragent='', $bottypr=0, $malicious=0) - функция используется для внутренних вызовов, она добавляет одно описание бота к текущую коллекцию (в памяти или в БД). Но также может быть явно вызвана программистом для добавления новых описаний ботов. Например, можно создать форму ввода для администратора сайта и результаты ввода заносить в базу этой функцией.

Параметры метода:

$botid - ИД бота, под которым он будет зарегистрирован.
$ipfrom - начальный IP адрес (в виде целого числа ! IP-адрес в октетной нотации "nnn.nnn.nnn.nnn" должен быть предварительно преобразован в integer с помощью метода FromIpToX32()).
$ipto - конечный адрес диапазона. Если пустой (ноль), будет приравнен к $ipfrom (т.е. диапапзон будет $ipfrom...$ipfrom).
$useragent - подстрока, по нахождению которой в user-agent можно идентифицировать данного бота. Например, "msn" для Microsoft MSN ботов.
$bottype - тип бота в соответствии с вашей классификацией.
$malicious - признак "зловредности" бота (0 или 1).

SetHandlerForBots($callbackfnc,$botlist) - регистрация функции, вызываемой из метода Dispatch() при детектировании одного из указанных ботов.

Параметры метода:

- строка с именем вызываемой функции.
$botlist - строка или массив, содержащий список ИД ботов, для которых вызывается данная функция. В строке идентификаторы ботов могут разделяться одним из символов "|" "," ";"
Следующие значения для $botlist эквивалентны : "msn|google|yahoo" и array('msn','google','yahoo').
Если Вы хотите зарегистрировать функцию для прочих (неопознанных) ботов, можно использовать вызов
SetHandlerForBots($callbackfnc,CBotRecognizer::UNDEFINED_BOT).

SetHandlerForTypes($callbackfnc,$bottype) регистрирует единую функцию-обработчик для всех ботов данного типа. Тип бота - это параметр, устанавливаемый Вами при регистрации бота, и здесь нет никаких ограничений. Например, можно назначить тип 0 индексирующим ботам, тип 1 - для сборщиков адресов email, тип 2 - для СПАМ-ботов и т.д., и когда все боты будут разбиты по типам, вы можете с помощью SetHandlerForTypes() задавать для них единые обработчики вместо (или вместе) того, чтобы указывать персональный обработчик для каждого бота. Если указан отдельный обработчик для обнаруженного бота, он будет вызван вместо единого обработчика типа.

SetMaliciousHandler($funcname) задает единую функцию, вызываемую для всех "зловредных" ботов (имеющих ненулевой флаг malicious). Если зарегистрировать такую функцию, она будет вызвана для любого злобного бота независимо от его ИД, типа и связанного с ним "персонального" обработчика.

IsMaliciousBot() возвращает true если опознанный бот имеет статус зловредного.

GetBotType() вернет тип обнаруженного бота. Понятно, что вызов методов SetHandlerForBots() и SetMaliciousHandler() имеет смысл только при использовании функции Dispatch(). Если Вы просто хотите получить ИД бота, вызвав самостоятельно GetBotId() (или его тип, или признак "зловредности"), функции SetHandlerForBots() и SetMaliciousHandler() вызывать нет смысла.

Dispatch() выполняет "диспетчирование" : он опознает ИД бота (а также тип и значение флага malicious) и вызывает соответствующую зарегистрированную для бота функцию (либо функцию для данного типа или malicious-ботов).
Если было задано "рабочее время" (worktime в конструкторе класса), Dispatch() будет "срабатывать на бота" только в указанный интервал времени.

GetErrorMessage() возвращает текст сообщения об ошибке, если она произошла при последней выполненной операции (либо пустую строку).

Вспомогательные функции



FromIpToX32($ipaddr) преобразует переданный IP адрес из "октетной" нотации (212.56.200.111) в соответствующее целое число. Аналог MySQL-функции INET_ATON().
Если переданый параметр не содержит точек, возвращается без преобразований.

FromX32ToIp($ipx32) - выполняет обратное преобразование (в "nnn.nnn.nnn.nnn"), аналог MySQL INET_NTOA().

Распространяется по лицензии BSD License

Ссылки

История изменений

1.00.001 (02.10.2009)


Copyright © Alexander Selifonov, www.selifan.ru