При написании одной доски объявлений, заказчик, как водиться, попросил написать парсер объявлений с известных досок. 

На современном уровне развития антиспам систем, сграбить новые объявления не так и просто с задачей я справился. Пришлось применить парочку хитрых приемов с COM объектом Интернет Эксплорера. Но сейчас не про это.

Грош цена такому тексту для поисковых систем. Ведь текст не уникальный, а значит надо сделать так, чтобы он стал уникальным.

Простейшая замена одного слоа на другое по базе не составит труда для php. Ведь php это текстовый язык программирования, созданный для текстовых вычислений и преобразований. Грубо говоря php это просто шаблонизатор. Но я опять отвлекся. 

В php есть множество функций для замены одного слова на другое. Рассмотрим несколько из них. 

Функция, которая первой приходит в голову это strtr 

<?php
$a = strtr( 'Мобильный телефон Samsung обменяю на новый iphone' ,array(
   'Мобилный телефон'=>'Мобильник',
   'Samsung'=>'Самсунг', 'новый'=>'новенький','обменяю'=>'поменяю',
   'Iphone'=>'Айфон'));
// Мобильник Самсунг поменяю на новенький Айфорн

удобно, но не совсем. Функция чувствительна к регистру. Значит Samsung и samsung это разные слова. 

По опыту скажу, что доски объявлений это то еще сборище афоризмов и цитат, что только там не пишут, и грамотностью разумеется не блещут. Те одноклассники или вконтакте отдыхают по сравнению с досками объявлений. Почитайте сами, очень интересно http://statuso.ru/bezkategorii/ 

Но я вновь отвлекся. 

У большинства текстовых функций php, есть регистронезависимый аналог с буквой i по середине, обычно он не дружит с utf-8. Но у strtr его нет.

Пожалуй единственная функция, которая корректно работает с utf-8 и регистронезависема это preg_replace

Разумеется, нужно указывать верные флаги iu - i - регистр неважен, u - работаем с utf-8

Перепишем верхний код  

<?php
$a = preg_replace(
  array('#мобилный телефон#ui','#samsung#ui', '#новый#ui','#Обменяю#ui','#Iphone#ui'),
  array('Мобильник','Самсунг','новенький','поменяю','Айфон'),
'Мобильный телефон Samsung обменяю на новый iphone');
// Мобильник Самсунг поменяю на новенький Айфорн

Уже более приемлемо. Но, вот хранить массив ключей в таком виде не очень удобно. 

Поэтому я написал вот эту функцию

function _strtr($str,$repl_array){
  $keys = array_map(function($key){
                  return '#'.$key.'#ui';
               },array_keys($repl_array)); 
  $values = array_values($repl_array);
  return preg_replace($keys,$values,$str);
}
echo _strtr('Мобильный телефон Samsung обменяю на новый iphone',
  array(
     'мобильный телефон'=>'Мобильник',
     'samsung'=>'Самсунг', 'новый'=>'новенький','обменяю'=>'поменяю',
     'Iphone'=>'Айфон'
   )
);

Статья была бы не полной, без рандомизатора. Текст должен получаться разным.

function _strtr($str,$repl_array){
  $keys = array_map(function($key){
                  return '#'.$key.'#ui';
               },array_keys($repl_array)); 
  $values = array_values($repl_array);
  $values = array_map(function($value){
                  $variants = explode('|', $value);
                  return $variants[rand(0, count($variants) - 1)];
               },array_values($repl_array));
  return preg_replace($keys,$values,$str);
}
echo _strtr('Мобильный телефон Samsung обменяю на новый iphone',
  array(
     'мобильный телефон'=>'Мобильник|Фага|Телефон|Сотовый',
     'samsung'=>'Самсунг|LG|Nokia', 'новый'=>'новенький|обновленный|хороший','обменяю'=>'поменяю|дам в размен',
     'Iphone'=>'Айфон|IP|Nokia 3210'
   )
);

Результаты

Фага LG дам в размен на хороший IP
Телефон Самсунг поменяю на новенький IP
Телефон Самсунг дам в размен на обновленный Nokia 3210

Вот так и творится история ;) Разнообразие текстов зависит только от Вашей фантазии и размера базы синонимов

UPD Рандомные ключи

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

Однако у нас учебные цели, поэтому будем вновь работать с массивом объектов. А о том как подключить это дело к базе, предлагаю обсудить в комментариях.

Итак, что мы хотели бы получить от нашей функции:

echo _strtr('Мобильный телефон Samsung обменяю на новый iphone',
  array(
     'мобильный телефон|Сотовый телефон|мобилка|Мобильник|Фага|Телефон|Сотовый',
     'samsung|Самсунг|LG|Nokia',
     'новый|новенький|обновленный|хороший',
     'обменяю|отдам|разменяю|поменяю|дам в размен',
     'Iphone|Айфон|Айфон|IP|Nokia 3210'
   )
);

Т.е. проходим по «базе» синонимов, если находим совпадение с одним из них, выбираем любой из оставшихся. Базу ключей отдельно содержать не надо.

Реализация

mb_internal_encoding("UTF-8");
function synonimize($str, $repl_array){
  $keys = array_map(function($key){
    return '#'.$key.'#ui';
  }, $repl_array); 
  
  foreach ($keys as $i=>$key) {
    $str = preg_replace_callback($key, function ($match) use ($repl_array, $i) {
      $syns = explode('|', $repl_array[$i]);
      array_splice($syns, array_search(mb_strtolower($match[0]), array_map('mb_strtolower', $syns)), 1);
      return $syns[rand(0, count($syns) - 1)];
    }, $str);
  }
  
  return $str;
}
echo synonimize('Мобильный телефон Samsung обменяю на новый iphone',
  array(
     'Сотовый телефон|мобилка|Мобильник|Фага|Телефон|Сотовый|мобильный телефон',
     'samsung|Самсунг|LG|Nokia',
     'новый|новенький|обновленный|хороший',
     'обменяю|отдам|разменяю|поменяю|дам в размен',
     'Iphone|Айфон|IP|Nokia 3210'
   )
);

Я изменил название функции, так как к strtr оно уже не имеет никакого отношения.

В самом верху добавлена функция mb_internal_encoding, которая устанавливает кодировку по умолчанию UTF-8. Если у вас другая кодировка, то ее надо убрать. И в модификаторах, в регулярном выражении убрать u

Функция, как и раньше, генерирует массив регулярных выражений. Затем перебирает каждое из них (не лучший подход, ниже объясню почему). При нахождении совпадений, вызывается анонимная callback функция. В ней, исходный набор синонимов разбивается на массив и при помощи array_search и array_splice из него удаляется найденное выражение ($match[0]).

После чего, используя функцию rand, из оставшихся синонимов выдергиваем случайный, и затем заменяем на него, найденное выражение($match[0]), вернув его в конце анонимной функции.

Теперь немного про preg_replace_callback. Вообще, она умеет получать на вход массив из регулярок. Тогда необходимость ручного перебора бы отпала. Это существенно ускорило бы работу скрипта. Но я не придумал, как в callback функции узнать какая из регулярок сработала. Можно конечно прогнать исходный массив синонимов циклом и найти в нем нужный набор синонимов, используя $match[0]. Это было бы быстрее, чем перебирать весь массив в начале. Пища для размышления, если у вас несколько млн синонимов.

Пока же главное, что оно работает. Я проверил, и получил несколько прекрасных выражений

Сотовый телефон Nokia поменяю на хороший Nokia 3210
мобилка Nokia дам в размен на новенький Айфон
Фага Nokia отдам на новенький Айфон

Всем добра! Пишите вопросы в комментариях. На наиболее интересные вопросы буду отвечать или даже дополнять статью.

Оставлять комментарии могут только зарегистрированные пользователи

Комментарии  

Ohotnik
# Ohotnik 24.07.2013 01:44
фигово чо то работает
Leroy
# Leroy 24.07.2013 13:51
что именно?
Антон Игоревич
# Антон Игоревич 01.07.2015 17:44
Не рандомизирует то))) , а просто то самое и выводит
Leroy
# Leroy 02.07.2015 08:33
Дополнил статью до рандомизатора
Евгений.
# Евгений. 14.01.2016 09:08
Тут получается что будет заменено только слово "новый" на указанные варианты
'новый'=>'новенький|обновленный|хороший'

Как сделать чтобы любое слово из диапазона менялось на любое слово 'новый|новенький|обновленный|хороший'
ну кроме на себя само.
Leroy
# Leroy 17.01.2016 22:42
Дополнил статью, спасибо за ваш вопрос!
kvarterion
# kvarterion 23.10.2016 14:17
Спасибо, очень помогли. Немного подкорректировал под себя, работает. Есть вопрос, надеюсь, поможете)
Я таким образом вывожу несколько предложений на странице. Контент получается динамическим. При обновлении страницы, естественно, подбираются разные слова. Как сделать это творение статичным? Например, при первом запросе к странице текст сформировался и сохранился. Mysql не использую.
kvarterion
# kvarterion 23.10.2016 14:18
Спасибо, очень помогли. Немного подкорректировал под себя, работает. Есть вопрос, надеюсь, поможете)
Я таким образом вывожу несколько предложений на странице. Контент получается динамическим. При обновлении страницы, естественно, подбираются разные слова. Как сделать это творение статичным? Например, при первом запросе к странице текст сформировался и сохранился.
Mysql не использую.