Поиск похожих текстов в базе данных MySQL + PHP

Поиск похожих текстов в базе данных MySQL + PHP

Один из вариантов поиска похожих статей в базе данных основан на схождении слов в двух текстах.

Данный алгоритм не обладает точным результатом, но позволяет выявить явные дубли.

Для поиска понадобится функция, которая очистит текст от html-кода, лишних слов и вернёт массив слов.

function get_minification_array($text)
{
	// Удаление экранированных спецсимволов
	$text = stripslashes($text);	
	
	// Преобразование мнемоник 
	$text = html_entity_decode($text);
	$text = htmlspecialchars_decode($text, ENT_QUOTES);	
	
	// Удаление html тегов
	$text = strip_tags($text);
	
	// Все в нижний регистр 
	$text = mb_strtolower($text);	
	
	// Удаление лишних символов
	$text = str_ireplace('ё', 'е', $text);
	$text = mb_eregi_replace("[^a-zа-яй0-9 ]", ' ', $text);
	
	// Удаление двойных пробелов
	$text = mb_ereg_replace('[ ]+', ' ', $text);
	
	// Преобразование текста в массив
	$words = explode(' ', $text);
	
	// Удаление дубликатов
	$words = array_unique($words);

	// Удаление предлогов и союзов
	$array = array(
		'без',  'близ',  'в',     'во',     'вместо', 'вне',   'для',    'до', 
		'за',   'и',     'из',    'изо',    'из',     'за',    'под',    'к',  
		'ко',   'кроме', 'между', 'на',     'над',    'о',     'об',     'обо',
		'от',   'ото',   'перед', 'передо', 'пред',   'предо', 'по',     'под',
		'подо', 'при',   'про',   'ради',   'с',      'со',    'сквозь', 'среди',
		'у',    'через', 'но',    'или',    'по'
	);

	$words = array_diff($words, $array);

	// Удаление пустых значений в массиве
	$words = array_diff($words, array(''));	

	return $words;
}
PHP

Пример работы функции:

$array = get_minification_array('
	<p>Сегодня существует огромное множество разнообразных диет, 
	отличающихся степенью строгости и результатами, которых с их 
	помощью добиваются худеющие. Существует одна эффективная диета 
	творог + яблоки, которая позволит легко скинуть 4 кг за 4 дня.</p>
');

print_r($array);
PHP

Результат:

Array
(
    [0] => сегодня
    [1] => существует
    [2] => огромное
    [3] => множество
    [4] => разнообразных
    [5] => диет
    [6] => отличающихся
    [7] => степенью
    [8] => строгости
    [10] => результатами
    [11] => которых
    [13] => их
    [14] => помощью
    [15] => добиваются
    [16] => худеющие
    [18] => одна
    [19] => эффективная
    [20] => диета
    [21] => творог
    [22] => яблоки
    [23] => которая
    [24] => позволит
    [25] => легко
    [26] => скинуть
    [27] => 4
    [28] => кг
    [31] => дня
)

Для более точного результата можно удалить из текста все стоп-слова.

Теперь, нужно получить проверяемую статью и сверить с остальными. В примере используется таблица `articles`, с полями `id`, `name`, `text`:

Пример таблицы со статьями

Поиск будет осуществляется для статьи `id` = 1, причём для теста в базе есть копия этой статьи с немного измененным текстом.

// Подключение к БД
$dbh = new PDO('mysql:dbname=db_name;host=localhost', 'логин', 'пароль');
 
// Получаем статью
$sth = $dbh->prepare("SELECT * FROM `articles` WHERE `id` = 1");
$sth->execute(array());
$article = $sth->fetch(PDO::FETCH_ASSOC);

// Получаем для неё массив слов
$text = get_minification_array($article['name'] . ' ' . $article['text']);

$count = count($text);	
$out = array();

// Проход по всем статьям в таблице
$sth = $dbh->prepare("SELECT * FROM `articles`");
$sth->execute();	
$list = $sth->fetchAll(PDO::FETCH_ASSOC);

foreach($list as $row) {
	$verifiable = get_minification_array($row['name'] . ' ' . $row['text']);

	$similar_counter = 0;
	foreach ($text as $text_row) {
		foreach ($verifiable as $verifiable_row){
			if($text_row == $verifiable_row) {
				$similar_counter++;
				break;
			}
		}
	}
	$out[$row['name']] = $similar_counter * 100 / $count;
}

// Сортировка результатов и ограничение до 15шт
arsort($out);
$out = array_slice($out, 0, 15, true);

print_r($out);
PHP

Результат поиска:

Array
(
    [Диета на яблоках и твороге] => 100
    [Диета на яблоках и твороге (test)] => 97,368421052632
    [Диета Ларисы Гузеевой] => 21,052631578947
    [Как похудеть в лице] => 19,736842105263
    [Соевый соус: польза и вред] => 17,105263157895
    [Сырная диета] => 14,473684210526
    [Какую обувь выбрать для сильных морозов?] => 13,157894736842
    [Какие мышцы работают на эллиптическом тренажере?] => 10,526315789474
    [Боль в коленном суставе при нагрузке] => 9,2105263157895
    [Польза и вред дыни для организма человека] => 9,2105263157895
    [Разгрузочная диета после праздников] => 9,2105263157895
    [Вредно ли загорать на солнце?] => 7,8947368421053
    [Йогуртовая диета] => 7,8947368421053
    [Как стирать пуховое одеяло?] => 7,8947368421053
    [Какие витамины в укропе] => 6,5789473684211
)

В результате видно, что статья схожа сама с собой на 100%, с тестовой статьей на 97%, с остальными не более 21%.

21.12.2020, обновлено 20.07.2022
7281

Комментарии 1

Kornej Kornej
21 января 2022 в 19:25
Прекрасное решение!
В идеале, добавить бы пару возможностей:
1. Прикрутить к какому-нибудь сервису, чтобы существительные и прилагательные переводил в именительный падеж, все прилагательные в мужской род, из множественного числа в единственное, и только после этого удалять дубли.
2. Если статьи после (int)$similar_counter * 100 / $count имеют одинаковые значения (или +-5, например), выводить такие одинаковые при загрузке страницы в рандомном порядке.

, чтобы добавить комментарий.

Другие публикации

Обрезка текста для анонса
В случаях где анонсы не заполнены, сформировать и вывести их можно несколькими способами...
8030
+4
Вставить баннер в центр статьи
Бывает так - есть сайт на CMS c множеством статей и нужно вставить в каждую статью баннер, лучше в ее центр т.к. в таком месте у баннера будет лучшая эффективность. Но не вставлять же в админке код...
4683
+2
Защита текстов от копирования
Примеры защиты от копирования текста с сайта, добавление копирайта при копировании и отключение выделения текста в браузере. Методы обхода этих ограничений.
12945
+6
Получить фото из Instagram без API
Так как Instagram и Fasebook ограничили доступ к API, а фото с открытого аккаунта всё же нужно периодически получать и...
24717
+7
Примеры использования PDO MySQL
В статье приведены основные примеры работы с расширением PHP PDO. Такие как подключение к БД, получение, изменение и...
104318
+8
Постраничный вывод и базы данных
В SQL запросах, для ограничения количества строк в результате используется инструкция LIMIT, например следующий вернёт...
17169
0