войти зарегистрироваться

Определяем «неправильные» слова при борьбе со спамом

При борьбе со спамом на форуме возникла идея автоматически отлавливать слова, внешне похожие на «нормальные», но фактически отличающиеся от шаблонных, имеющихся в базе стоп-слов. Делается это путём замены кириллических символом на латиницу и наоборот. Например, «Пpoдaeм бeтoн» и «Продаем бетон» только внешне выглядят одинаково, а на самом деле они отличаются друг от друга.
Вот я и написал небольшую функцию для уменьшения энтропии Вселенной, которая определяет (если сможет) язык, на котором написано слово, и заменяет его на нормальное. Затем проверяем это слово по списку стоп-слов и принимаем решение, запрещённое оно или нет :)

(Красным цветом помечается кириллица, синим- латиница или цифры)

В подробности кода на C# вдаваться не буду, думаю, что всё будет понятно.
Конструктивным предложениям и замечаниям буду рад :)

public string CheckWord(string word, out string lang, out bool Changes)
{
  lang = "?";
  string newword = word;
  string OnlyRu = "БбвГгДдЁёЖжЗзИиЙйЛлмнПптУФфЦцЧчШшЩщЪъЫыЬьЭэЮюЯя";
  string OnlyEn = "DdFfGghIiJjLlNQqRrSstUVvWwYZz";
  string Rus = "АаВЕеКкМНОоРрСсТуХхЗО1тиа@пь";
  string Eng = "AaBEeKkMHOoPpCcTyXx30imu@anb";

  bool IsRu100percent = false;
  foreach (char c1 in word)
    foreach (char c2 in OnlyRu)
      IsRu100percent = IsRu100percent || (c1 == c2);
  
  if (IsRu100percent)
  {
    lang = "ru";
    
    //Конвертируем все сомнительные символы в русские
    for (int i = 0; i < word.Length; i++)
      if (Eng.IndexOf(word[i]) >= 0)
      {
        //помечаем word[i] как "фальшивый"
      }
    for (int i = 0; i < Rus.Length; i++)
      newword = newword.Replace(Eng[i], Rus[i]);
  }
  else
  {
    bool IsEn100percent = false;
    foreach (char c1 in word)
      foreach (char c2 in OnlyEn)
        IsEn100percent = IsEn100percent || (c1 == c2);
    if (IsEn100percent)
    {
      lang = "en";
      //Конвертируем все сомнительные символы в английские
      for (int i = 0; i < word.Length; i++)
        if (Rus.IndexOf(word[i]) >= 0)
        {
          //помечаем word[i] как "фальшивый"
        }
      
      for (int i = 0; i < Eng.Length; i++)
        newword = newword.Replace(Rus[i], Eng[i]);
      
    }
  }
  //Были ли замены?
  Changes = newword != word;
  newword = newword.ToLower();
  _SelectionColor = lang == "ru" ? Color.Red : lang == "en" ? Color.Blue : Color.Black;
  return newword;
}

* This source code was highlighted with Source Code Highlighter.



Вызов происходит просто:
string lang;
bool changes;
string re = "[\\w\\@]+";
Regex rx = new Regex(re, RegexOptions.IgnoreCase | RegexOptions.Singleline);
Match m = rx.Match("Пpoдaeм бeтoн");
while (m.Success)
{
  string newWord = CheckWord(m.ToString(), out lang, out changes);
  m = m.NextMatch();
}

* This source code was highlighted with Source Code Highlighter.

комментарии (31)

  • Оно конечно хорошо, но как это использовать в реальной жизни?
    В голову приходит только написание какого-нибудь плагина для аутглюка, который будет делать предварительное преобразование неправильных слов в правильные во входящих письмах до срабатывания фильтра на ключевые слова…
    • можно написать PS-скрипт для Exchange, например.
    • А еще в фильтр комментов к разным блогам можно!
      На php, как я понял, портировать ее просто.
  • для большей применимости в реальной жизни, программу надо бы сделать консольной. И под *nix порт нужен, т.к. большинство релеев крутится под *nix.
    • Для большей применимости — я дал код функции. Хотите, в консоль делайте, хотите — в dll, хотите — переводите в *nix, на питон, на php… Я просто показал идею.
      Оконный вид программы с подсветкой слов — исключительно для наглядности работы функции.
      • Что ж, спасибо за идею.
  • К сожалению, это только один из многих частных случаев.
    В_И_А_Г_Р_А
    Виа(гра)
    ВИ/\ГР/\
    Не говоря уже о том, что много спама валит без прямого указания там ключевых слов.

    Пока компы не научатся понимать смысл текста (читай: пока не будет создан ИИ), спамеры всегда смогут придумать как обойти подобные фильтры.

    Но все равно спасибо, что занимаетесь этой темой. Надо держать спамеров в тонусе :)
    • Вряд ли кто-то серьезно рассчитывает создать абсолютный фильтр. Спасает так называемый «барьер» — небольшие усилия помогают отстеять огромную массу любителей-спамеров. Дальше повышать барьер сложнее и процент отсеянных спамеров увеличивается намного медленнее. Главное — решить, где остановиться.

      На примере: фильтровал я как-то комментарии на сайте развлекательной тематики. Простой список стоп-слов позволили заблокировать процентов 90 нарушителей. После того как я перед проверкой стал удалять все символы кроме букв и цифр (тем самым отсекая варианты типа «В_И_А_Г_Р_А» или «Виа(гра)»), стало отсеиваться 98%. Остальные проверки были совсем специфическими и добавлялись на конкретные случаи, под настроение. В принципе, это — вариант, на котором можно остановится, все остальное пока проще изредка вычищать вручную.
  • Капитан компилятор сказал что у переменной string OnlyRu в конце не стоит ";"
    • Спасибо, поправил.
      • А его друг другой капитан когда присмотрелся сказал что 6 похоже на русскую «б»
        • Можете сами дополнить словарь по своему усмотрению.
          Ещё подскажу: «m» похожа на маленькую русскую «т», но это, как и «6»-«б» бросается в глаза.
          • Спамеры вконтакте спокойно используют «k» вместо «к», и не стесняются.
          • k и к как и З и 3 точно так же бросаются. Однакож отмечены в сабже. Зато тот кто будет машинально копировать из поста будет иметь «большую» защиту только не знаю от чего)
            • Добавил «1тиа@», «imu@a» и убрал k/к из 100%-алфавита.
              И обновил скриншот
              • Добавил тему в избранное чтобы был справочник сопоставления букв)
                • Вообще, надо бы расширить функцию на работу с двусимвольными комбинациями.
                  Например, «bl» = «Ы», "/\" — «Л»
                  • Сделай просто массив, а не стринг.
  • Хм, а почему бы не забраковывать письма в которых есть более некоторого количества слов, в которых используется вперемешку латинские и русские буквы?
    Собственно сам факт того что в слове из 5 букв русского алфавита есть две буквы из латинского уже определяет то что слово — паразит.
    • Помоему наиболее разумное решение. Как минимум внимания таким письмам должно больше уделяться.
  • спасибо. я сам хотел написать такой код, но руки не доходили. На моей работе это нужно не для спама, а для проверки написания товара. У нас блондинки умудряются завести в базе 1С товар вперемежку. Например, есть модель CT-404 (буквы англ.), а заводят русскими. А если CN-102, то первую букву напишут русской, а потом, так как в русской раскладке буквы N нет, то переключаются на английскую раскладку и вводят уже второй символ. Бардак в базе был еще тот.
  • Кстати, отсеять практически весь «нигерийский» спам можно всего лишь по одному (или трем) ключевым словам, практически не встречающимися в нормальных текстах — это «kin» или «next of kin» (наследник).
    • Нигерийский спам самый нигерийский спам в спаме)
  • Зря вы так на тему спам-фильтров: наивный байсековский фильтр и фильтр на основе метода Фишера (SmapBayes, подключаемый к Outlook как раз на основе Фишера) могут и используют признак смешение букв из разных алфавитов как признак спама, хотя затем можно нормализовывать текст по описанному выше методу и подвергать другим тестам: ключевые слова и словосочетания, регистр слов (к примеру, все буквы в верхнем регистре и таких «кричащих» слов множество), анализ метаинформации: кто отправил письмо, откуда и т.д… Если же сразу нормализовывать текст, то можно потерять множество признаков, по которым мог бы обнаружен спам.

    А за наглядный пример нормализации текста спасибо.
  • Спамеры такие спамеры
  • Можно еще считать слова с подменой букв как заведомо спамерские.
  • Я ещё проще придумал. Если в слове происходит смена кодировки, то это спам!
    • Кодировки???
      Уж не языка-ли?
  • за этот файл похожих символов юникода два года назад я получил 500 руб. от своего друга
    narod.ru/disk/18479522000/!likes.xml.html
  • Ещё немного изменил строчки:
    string Rus = «АаВЕеКкМНОоРрСсТуХхЗО1тиа@пь930»;
    string Eng = «AaBEeKkMHOoPpCcTyXx30imu@anbgEo»;

Только авторизованные пользователи могут оставлять комментарии. Авторизуйтесь, пожалуйста.