PHP фильтр текста для запрещенных слов

У нас есть веб-сайт C2C, и мы не поощряем продажу фирменных продуктов на нашем веб-сайте. Мы построили базу данных фирменных слов, таких как Nike и D & G и сделал алгоритм, который фильтрует информацию о продукте для этих слов и отключает продукты, если он содержит эти слова.

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

  • Я Мир nike
  • у меня есть N ikee обувь
  • у меня есть nikeeshoes
  • Я продаю корпуса i-phone
  • Я продаю iphone-корпуса
  • вы можете иметь iphone

теперь проблема в том, что он также ловит следующее:

  • быстрая швейная фабрика (для D&G)
  • rosNIK Electronics (для Nike)

что может быть сделано для предотвращения таких ложных совпадений при сохранении эффективности с ловлей истинных случаев?

редактировать

вот код для тех из вас, кто лучше понимать код:

$orignal_txt = preg_replace('/&.{0,}?;/', '', (strip_tags($orignal_txt)));
$orignal_txt_nospace = preg_replace('/W/', '', $orignal_txt);
{
    $qry_kws = array("nike", "iphone", "d&g");
    foreach($qry_kws as $rs_kw)
    {       
        $no_space_db_kw = preg_replace('/W/', '', $rs_kw);
        if(stristr($orignal_txt_nospace, $rs_kw))
        {
            $ipr_banned_keywords[] = strtolower($rs_kw);
        }
        else if(stristr($orignal_txt_nospace, $no_space_db_kw))
        {
                $ipr_banned_keywords[] = strtolower($rs_kw);
        }

    }
}

4 ответов


просто играю .... (Не использоваться в производстве)

$data = array(
        "i am nike world",
        "i have n ikee shoes",
        "i have nikeeshoes",
        "i sell i-phone casings",
        "i sell iphone-casings",
        "you can have iphone",
        "rapiD Garment factor",
        "rosNIK Electronics",
        "Buy you self N I K E",
        "B*U*Y I*P*H*O*N*E BABY",
        "My Phone Is not available");


$ban = array("nike","d&g","iphone");

Пример 1:

$filter = new BrandFilterIterator($data);
$filter->parseBan($ban);
foreach ( $filter as $word ) {
    echo $word, PHP_EOL;
}

выход 1

rapiD Garment factor
rosNIK Electronics
My Phone Is not available

Пример 2

$filter = new BrandFilterIterator($data,true); //reverse filter
$filter->parseBan($ban);
foreach ( $filter as $word ) {
    echo $word, " " , json_encode($word->getBan()) ,  PHP_EOL;
}

выход 2

i am nike world ["nike"]
i have n ikee shoes ["nike"]
i have nikeeshoes ["nike"]
i sell i-phone casings ["iphone"]
i sell iphone-casings ["iphone"]
you can have iphone ["iphone"]
Buy you self N I K E ["nike"]
B*U*Y I*P*H*O*N*E BABY ["iphone"]

Класс

class BrandFilterIterator extends FilterIterator {
    private $words = array();
    private $reverse = false;

    function __construct(array $words, $reverse = false) {
        $this->reverse = $reverse;
        foreach ( $words as $word ) {
            $this->words[] = new Word($word);
        }
        parent::__construct(new ArrayIterator($this->words));
    }

    function parseBan(array $ban) {
        foreach ( $ban as $item ) {
            foreach ( $this->words as $word ) {
                $word->checkMetrix($item);
            }
        }
    }

    public function accept() {
        if ($this->reverse) {
            return $this->getInnerIterator()->current()->accept() ? false : true;
        }
        return $this->getInnerIterator()->current()->accept();
    }
}


class Word {
    private $ban = array();
    private $word;
    private $parts;
    private $accept = true;

    function __construct($word) {
        $this->word = $word;
        $this->parts = explode(" ", $word);
    }

    function __toString() {
        return $this->word;
    }

    function getTrim() {
        return preg_replace('/\W/', '', $this->word);
    }

    function accept() {
        return $this->accept;
    }

    function getBan() {
        return array_unique($this->ban);
    }

    function reject($ban = null) {
        $ban === null or $this->ban[] = $ban;
        $this->accept = false;
        return $this->accept;
    }

    function checkMetrix($ban) {
        foreach ( $this->parts as $part ) {
            $part = strtolower($part);
            $ban = strtolower($ban);
            $t = ceil(strlen(strtolower($ban)) / strlen($part) * 100);
            $s = similar_text($part, $ban, $p);
            $l = levenshtein($part, $part);
            if (ceil($p) >= $t || ($t == 100 && $p >= 75 && $l == 0)) {
                $this->reject($ban);
            }
        }
        // Detect Bad Use of space
        if (ceil(strlen($this->getTrim()) / strlen($this->word) * 100) < 75) {
            if (stripos($this->getTrim(), $ban) !== false) {
                $this->reject($ban);
            }
        }
        return $this->accept;
    }
}

простой, сделать матч бренда, прежде чем удалить пробелы/специальные символы. Тогда это не будет соответствовать этим странным случаям edge.


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

хотя мы не обсуждаем фильтр навязчивости здесь, это почти такая же концепция, так что вы были бы хорошо посоветовал почитать о некоторых из худших ошибок, допущенных фильтрами obsenity.

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

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

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


вот только идея.

Почему бы вам не сделать соответствие первым, и если он попадает в "фирменный" фильтр, он попадает в очередь обзора для вас, чтобы принять / отклонить, выделив совпадения для легкого обнаружения.

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

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