Pull to refresh

Пишем фильтр «плохих» слов

Reading time5 min
Views92K
Многие из вас наверняка не раз бывали в публичных многопользовательских чатах. Будь то форум, вебчат или чат-сервер в локалке провайдера, идеального порядка можно достичь лишь при малом количестве пользователей (не более сотни, на мой взгляд). Когда сообщество разрастается, возникают примерно одни и те же проблемы: мат, спам и флуд — один скучающий индивид каждую минуту постит «Все в кантру!», другой матерится по поводу и без и все в таком духе.

Идеального мат-фильтра до сих пор не изобретено. Но мы этим заниматься и не будем, рассмотрим реализацию необходимого минимума.


Мат


Конечно же мы на нем разговариваем, кто же спорит :). С моей точки зрения мат может иметь место для красного словца или выражения пущей экспрессии. Но далеко не все столь легко его воспринимают и с этим необходимо мирится (или создавать свое сообщество а-ля «матерись сколько влезет»). Особенно неприглядно выглядит поток слабомотивированного мата от не слишком умных / воспитанных граждан. Ну это оставим на их совести.

Русский язык очень богат возможностями слово и фразообразования, поэтому матерных конструкций придумать можно очень много — порой диву даешься. Так что мы (пока) не будем пытаться бороться с этими явлениями. Решение в лоб — фильтр по словарю. Сам словарь, думаю, не составит большого труда собрать. У меня вышло где-то 250 слов :).

Но первые же опыты показывают — это метод не дает положительного результата. Юзеры обучаются обходить его крайне быстро, видя неудачи пары первых погорельцев. Как они это делают? В основном вариантов несколько:
  • Замена согласных на парные созвучные им (например «х» на «к»);
  • Замена букв на аналогично выглядящие латинские («п» — «n», «и» — «u»);
  • Смешение кириллицы и транслита;
  • Употребление псевдографики.

На помощь приходят регулярные выражения. Создадим таблицу замены символов с учетом вышесказанного.

(
  'а' => ['а', 'a', '@'],
  'б' => ['б', '6', 'b'],
  'в' => ['в', 'b', 'v'],
  'г' => ['г', 'r', 'g'],
  'д' => ['д', 'd', 'g'],
  'е' => ['е', 'e'],
  'ё' => ['ё', 'е', 'e'],
  'ж' => ['ж', 'zh', '*'],
  'з' => ['з', '3', 'z'],
  'и' => ['и', 'u', 'i'],
  'й' => ['й', 'u', 'y', 'i'],
  'к' => ['к', 'k', 'i{', '|{'],
  'л' => ['л', 'l', 'ji'],
  'м' => ['м', 'm'],
  'н' => ['н', 'h', 'n'],
  'о' => ['о', 'o', '0'],
  'п' => ['п', 'n', 'p'],
  'р' => ['р', 'r', 'p'],
  'с' => ['с', 'c', 's'],
  'т' => ['т', 'm', 't'],
  'у' => ['у', 'y', 'u'],
  'ф' => ['ф', 'f'],
  'х' => ['х', 'x', 'h', 'к', 'k', '}{'],
  'ц' => ['ц', 'c', 'u,'],
  'ч' => ['ч', 'ch'],
  'ш' => ['ш', 'sh'],
  'щ' => ['щ', 'sch'],
  'ь' => ['ь', 'b'],
  'ы' => ['ы', 'bi'],
  'ъ' => ['ъ'],
  'э' => ['э', 'е', 'e'],
  'ю' => ['ю', 'io'],
  'я' => ['я', 'ya'],
)


* This source code was highlighted with Source Code Highlighter.


Теперь нужно сделать из нашего словаря библиотеку регулярных выражений. В принципе их можно конструировать и налету. Принцип анализа прост: на месте буквы может быть любая последовательность из таблицы. Чтобы народ не хитрил вставляя пробелы, точки и т.п. между буквами для обхода фильтра — их появление между буквами тоже учитываем. Ну и чтобы не было ложных срабатываний будем отрабатывать только слово целиком, без учета словообразования сложных конструкций.

Да, последнее конечно несколько снижает эффективность фильтра, но все-таки видоизмененные слова уже не очень-то и мат, а основную массы базовых слов и фраз мы можем отрезать словарем. В принципе если срабатывание фильтра необходимо только для скрытия неприглядных слов (а не для немедленного бан-хаммера :)), то это условие можно отбросить.

В итоге для всем известного слова из трех букв регулярка будет выглядеть примерно следующим образом:

m/(?:^|\W)((?:х|x|h|к|k|}{)\W*[уyu]\W*[йuyi])(?:$|\W)/ig;


В общем суть проста: делаем словарь, из него генерируем библиотеку регулярок и применяем их в нашем фильтре.

Недостатки подхода:
  • Необходим большой словарь;
  • Не учитывается морфология русского языка;
  • Возможны ложные срабатывания («Куй железо пока горячо!»)

Т.е. в общем-то это не панацея, но определенный эффект сдерживания имеет.

Спам и флуд


Флуд или бессмысленный поток символов, смайлов, предложений может порядком надоедать. Спамом в нашем контексте мы также будем считать разновидность флуда, повторяющиеся фразы вроде «Фсе ф кантру! Создано на x.x.x.x» или «Народ, заходим на мою супер мегастраничку link». Впрочем эти явления больше характерны для чатов.

Отслеживать все это дело можно чисто механически, без применения какого-либо семантического анализа текста — а есть простые методы это сделать?

С флудом повторяющимися символами и смайлами в общем-то все понятно: подсматриваем в живом чате самые частые схемы флуда и используя квантификаторы их моделируем. Лишь некоторые примеры:

/(.)\\1{15,}/is;
/(.{3,})\\1{5,}/is;
/(\n|\r|\r\n)\\1{3,}/is;


Сложнее дело обстоит с повторами фраз. Подход в лоб довольно примитивен: сравниваем n-последних сообщений пользователя и в случае совпадений «срабатываем».

Первейший способ сопротивления — добавляем пробелы в строку, меняем количество знаков препинания (точек, восклицательных знаков). Впрочем, мы ведь можем сделать нормализацию строки, учитывающую такие изменения: например, просто убираем все лишнее.

Однако, тут нас ждет неудача со второй волной сопротивления: фразу начинают слегка менять, добавлять междометия, вводные слова и т.п. Вот тут уже пригодятся алгоритмы нечеткого сравнения.

Например можно вычислять Расстояние Левенштейна. Реализация этого алгоритма не очень сложна и есть на большинстве популярных языков, а в php так вообще присутствует нативно. Фактически, он возвращает разницу строк в количестве правок, необходимых для получения второй строки из первой.

Далее дело техники: определяем минимальный размер сообщения, на который стоит реагировать, считаем расстояние и если оно не превышает, например, 20% от длины сообщения, то считаем его повтором! Все просто. Длину сообщения и порог расстояния подбираем эмпирически, чтобы не мешать обычным пользователям.

Этот метод, конечно же, поможет только в противодействии скучающих индивидов, рассылающих все вручную (отсюда и малые правки, и повторяемость фраз). Если же пользователь шлет разнородные сообщения с пугающей скоростью, то скорее всего это спам-бот и тут нужны другие подходы или действия живого модератора.

Итог


Итак, мы рассмотрели в общих чертах методы противодействия мату, флуду и спаму. Предложенные методы наиболее актуальны для чат-систем коротких сообщений. Во многом простота подходов обусловлена применением такой в общем-то довольно непростой вещи как регулярные выражения.

Существуют и альтернативные методы, включающие попытки морфологического и семантического анализа фраз. Однако, если следовать идеологии того же AIML, человекоподобное поведение можно получить вполне эмпирически, не встраивая между вводом и выводом «слишком много компьютера».

Надеюсь информация будет кому-либо полезной. Буду рад любым комментариям. Как появится время, постараюсь представить на суд общественности собственную реализацию вышеописанных действий.
Tags:
Hubs:
+40
Comments79

Articles

Change theme settings