Похожие статьи без плагина

Долго я обещал сделать скрипт для создания виджета похожих статей без плагина на WordPress и наконец-то мои руки дошли до этого. Вообще сам по себе скрипт был написан мной уже достаточно давно и применяется с тех пор на моих проектах, но то скрипт! Статью же про него написать это уже отдельная история и сегодня я вам о ней поведаю. Примененный мной алгоритма поиск похожих статей не является кардинально новым, в том смысле, что идея довольно очевидна, потому, думаю, я не первый. Но тем не менее, подобной реализации под WordPress я еще не видел нигде, так что встречайте.

Немного о печеньках

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

Плюс хорошей фичей является возможность вывода описания текста в виде отрывка из его начала (сниппета). Кому такой подход не нравится тот конечно может его и не применять, но, мне кажется, что это принесёт только пользу. Во-первых, читателя могут заманить ключевые фразы из описания (которые при правильной оптимизации текста всегда будут находится в самом начале и будут попадать в этот сниппет), что продлит его пребывание на вашем сайте, увеличит просмотры, и как следствие, конверсию. Во-вторых, это увеличит общий объём вашего контента на странице, да ещё и насытит его похожими ключевыми словами (т.к. посты-то похожие), что безусловно понравится поисковикам. Так что в свете этих двух фактов, я рекомендую включить описательные сниппеты для похожих статей, хотя бы короткие.

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

Код функции

Ближе к делу. Код функции получился большой, так что не пугайтесь, всё таки он проводит небольшой контент-анализ поста. Весь ниже представленный код вам нужно разместить в functions.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/*
* функция нахождения похожих статей
*
* @param integer $id — идентификатор анализируемого поста
*/

function sp_related_posts($id){
global $wpdb;

$countArticle = 5;  // количество похожих статей
$isDescription = true;  // описание для каждой статьи
$descriptionLen = 200;  // если включено описание, то какой длинные его делать

$countWords = 6;    // количество слов в конечном итоге для анализа
$minLengthWord = 3; // минимальная длинна анализируемого слова, остальные отсеиваются
$factorTitle = 0.2; // эмпирический коэффициент участвующий в расчёте веса слов из заголовка
$factorText = 0.9;  // эмпирический коэффициент участвующий в расчёте веса слов из текста
$stopWords = array(‘они’, ‘том’, ‘тот’, ‘обо’, ‘она’, ‘три’, ‘без’, ‘ста’, ‘сто’, ‘них’, ‘там’, ‘тем’, ‘тут’, ‘оба’, ‘той’, ‘оно’, ‘под’, ‘сам’, ‘чьё’, ‘эта’, ‘эти’, ‘про’, ‘ней’, ‘это’, ‘чье’, ‘ото’, ‘ним’, ‘час’, ‘при’, ‘чей’, ‘чем’, ‘чья’, ‘что’, ‘уже’, ‘всю’, ‘ему’, ‘его’, ‘для’, ‘два’, ‘еще’, ‘ещё’, ‘изо’, ‘ибо’, ‘нет’, ‘год’, ‘где’, ‘вас’, ‘вам’, ‘был’, ‘вне’, ‘вот’, ‘вся’, ‘всё’, ‘все’, ‘или’, ‘эту’, ‘мой’, ‘мог’, ‘мне’, ‘так’, ‘над’, ‘нам’, ‘неё’, ‘нее’, ‘наш’, ‘нас’, ‘кто’, ‘мои’, ‘как’, ‘раз’, ‘моя’, ‘буду’, ‘этой’, ‘свое’, ‘тема’, ‘того’, ‘темы’, ‘тему’, ‘теме’, ‘твой’, ‘себе’, ‘этом’, ‘чуть’, ‘тебя’, ‘чтоб’, ‘куда’, ‘свою’, ‘чего’, ‘хотя’, ‘хоть’, ‘часу’, ‘этот’, ‘тоже’, ‘меня’, ‘могу’, ‘либо’, ‘ином’, ‘иной’, ‘надо’, ‘него’, ‘пять’, ‘сами’, ‘оный’, ‘один’, ‘ноль’, ‘есть’, ‘если’, ‘быть’, ‘ведь’, ‘было’, ‘были’, ‘была’, ‘весь’, ‘вниз’, ‘день’, ‘даже’, ‘всех’, ‘всей’, ‘свой’, ‘лишь’, ‘себя’, ‘семь’, ‘стал’, ‘из-за’, ‘кроме’, ‘можно’, ‘какой’, ‘через’, ‘таких’, ‘разве’, ‘когда’, ‘лучше’, ‘какое’, ‘потом’, ‘может’, ‘такою’, ‘такую’, ‘такие’, ‘перед’, ‘таком’, ‘также’, ‘между’, ‘вдоль’, ‘много’, ‘такое’, ‘точно’, ‘стали’, ‘стать’, ‘будем’, ‘давай’, ‘среди’, ‘всего’, ‘стала’, ‘видно’, ‘тогда’, ‘внизу’, ‘давно’, ‘сразу’, ‘здесь’, ‘знать’, ‘иметь’, ‘назад’, ‘зачем’, ‘таким’, ‘долго’, ‘такой’, ‘будут’, ‘такая’, ‘снова’, ‘тысяч’, ‘самим’, ‘опять’, ‘около’, ‘чтобы’, ‘после’, ‘самой’, ‘темою’, ‘будет’, ‘будто’, ‘редко’, ‘шесть’, ‘самая’, ‘часто’, ‘очень’, ‘темой’, ‘жизнь’, ‘самый’, ‘пусть’, ‘темам’, ‘вдруг’, ‘самым’, ‘этого’, ‘прямо’, ‘некто’, ‘никто’, ‘равно’, ‘какая’, ‘темах’, ‘почти’, ‘более’, ‘только’, ‘четыре’, ‘самому’, ‘десять’, ‘вместо’, ‘будете’, ‘теперь’, ‘девять’, ‘далеко’, ‘всегда’, ‘будешь’, ‘ребята’, ‘восемь’, ‘внутри’, ‘вокруг’, ‘давать’, ‘похоже’, ‘однако’, ‘иногда’, ‘такого’, ‘ничего’, ‘сказал’, ‘хорошо’, ‘нельзя’, ‘тысячу’, ‘тысячи’, ‘темами’, ‘тысяча’, ‘такому’, ‘больше’, ‘отчего’, ‘почему’, ‘потому’, ‘совсем’, ‘сейчас’, ‘другие’, ‘именно’, ‘другой’, ‘нибудь’, ‘каждым’, ‘такими’, ‘другое’, ‘каждый’, ‘большой’, ‘каждому’, ‘которые’, ‘конечно’, ‘человек’, ‘сначала’, ‘каждого’, ‘впрочем’, ‘кажется’, ‘никогда’, ‘сказали’, ‘сказать’, ‘недавно’, ‘сегодня’, ‘который’, ‘говорил’, ‘говорят’, ‘спасибо’, ‘говорим’, ‘другого’, ‘наконец’, ‘сказала’, ‘особенно’, ‘говорили’, ‘навсегда’, ‘говорить’, ‘которого’, ‘несколько’, ‘достаточно’, ‘пожалуйста’);
$stopSymbols = array(‘—’, ‘–’, ‘_’, ‘»‘, ‘‘’, ‘’’, ‘‚’, ‘“’, ‘”’, ‘„’, ‘»‘, ‘x27’, ‘x22’, ‘x60’, ‘\t’, ‘\n’, ‘\r’, ‘\», ‘,’, ‘.’, ‘/’, ‘\\’, ‘«’, ‘»’, ‘#’, ‘;’, ‘:’, ‘@’, ‘~’, ‘[‘, ‘]’, ‘{‘, ‘}’, ‘=’, ‘-‘, ‘+’, ‘)’, ‘(‘, ‘*’, ‘&’, ‘^’, ‘%’, ‘$’, ‘<‘, ‘>’, ‘?’, ‘!’);

$post = get_post($id);
$title = $post->title;
$text = $post->post_content;

$text = strip_tags($title.‘ ‘.$text);
// вначале убираем все стоп-символы и лишние пробелы из текста
foreach ($stopSymbols as $symbol)
$text = str_replace($symbol, ‘ ‘,$text);
// переводим текст в массив, разделяя элементы пробелом
$text = explode(‘ ‘, mb_strtolower(trim($text)));
// убираем все стоп-слова
$text = array_diff($text, $stopWords);
// считаем частоту повторения каждого слова
$text = array_count_values($text);
arsort($text);
$text = array_keys($text);
// убираем окончания
$words=array();
foreach($text as $word) {
if (strlen($word) >= $minLengthWord) {  // оставляем слова только длинной больше $minLengthWord
// От слов длиннее 7 символов отрезать 2 последних буквы
if (strlen($word) > 7) {
$word=substr($word,0,(strlen($word)2));
}
// От слов длиннее 5 символов отрезать последнюю букву
elseif (strlen($word) > 5) {
$word=substr($word,0,(strlen($word)1));
}
$words[]=addcslashes(addslashes($word),‘%_’);
}
}

$words = array_slice($words, 0, $countWords);

$where = «»;
$select = «(«;
// Условия для каждого из слов
foreach($words as $word) {
$select .= «IF (post_title LIKE ‘%».$word.«%’, «.$factorTitle.«, 0) + IF (post_content LIKE ‘%».$word.«%’, «.$factorText.«, 0) + «;
$where .= «post_title LIKE ‘%».$word.«%’ OR post_content LIKE ‘%».$word.«%’ OR «;
}
$select = substr($select, 0, 2);   // убираем последние два символа (плюсик и пробел)
$select.=«) AS relevant»;
$where = substr($where, 0, 3); // убираем последние три символа (OR и пробел)

$results = $wpdb->get_results(
SELECT
ID, post_title, ‘
.($isDescription?‘post_content, ‘:»).$select.
FROM
.$wpdb->posts.
WHERE
(‘
.$where.‘ ) AND post_status = «publish» AND post_type = «post» AND ID <> ‘.$id.
ORDER BY
relevant DESC
LIMIT ‘
.$countArticle
);

if (!empty($results)) {
$return = ‘<div class=»related-posts»><ul>’;
foreach($results as $result){
$return .= ‘<li><a title=»‘.$result->post_title.‘» href=»‘.get_permalink($result->ID).‘»>’.$result->post_title.‘</a>’;
if ($isDescription and $descriptionLen > 0 and $result->post_content) { // если описание для статей нужно, то вставляем его убрав лишние теги сократив до нужной длинны до пробела
$descriptionSrc = strip_tags($result->post_content);
$len = (mb_strlen($descriptionSrc) > $descriptionLen)
? mb_strripos(mb_substr($descriptionSrc, 0, $descriptionLen), ‘ ‘)
: $descriptionLen
;
$description = mb_substr($descriptionSrc, 0, $len);
$description = (mb_strlen($descriptionSrc) > $descriptionLen)
? $description . ‘…’
: $description
;

$return .= ‘ — <span class=»post-description»>’.$description.‘</span>’;
}
$return .= ‘</li>’;
}
$return .= ‘</ul></div>’;
echo $return;
}
}

Если вы не знаете, как вставлять код или у вас возникли проблемы с этим, то читайте статью о вставке кода в functions.php.

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

  • $countArticle — переменная для обозначения максимального количества похожих статей, которое вам нужно получить. По умолчанию равно 5, т.е. вам выведутся 5 похожих постов.
  • $isDescription — булеановская переменная, отвечающая за вывод описательного сниппета к каждой статье. Если true — сниппет будет выводится, false — не будет.
  • $descriptionLen — переменная отвечающая за длину выводимого сниппета. Она имеет смысл только когда $isDescription = true, в противном случае эта переменная не используется. Прошу обратить внимание, что сниппет обрезается на последнем пробеле перед превышением максимальной разрешённой длинны. Это значит, что у вас не будет оборванных на середине слов.
  • $countWords — переменная отвечающая за максимальное количество слов, которое будет отобрано из всего анализируемого текста для подбора похожих статей. Играясь с этой переменной вы можете улучшить (ухудшить) результата работы функции.
  • $minLengthWord — переменная отвечающая за минимальную длину анализируемого слова, все слова короче будут автоматически отброшены. Она необходима, чтобы ускорить работу функции и откинуть лишние короткие слова. Играясь с этой переменной вы можете улучшить (ухудшить) результата работы функции. Хотя по умолчанию она равна 3, что, я думаю, довольно компромиссно.
  • $factorTitle — переменная содержащая эмпирический коэффициент для расчёта веса слов из заголовка поста в запросе в базу. Ей можно присваивать любые числа. Играясь с этой переменной вы можете улучшить (ухудшить) результата работы функции.
  • $factorText — переменная содержащая эмпирический коэффициент для расчёта веса слов из текста поста в запросе в базу. Ей можно присваивать любые числа. Играясь с этой переменной вы можете улучшить (ухудшить) результата работы функции.
  • $stopWords — массив со стоп-словами, которые отсеиваются на этапе контент-анализа текста. Более подробно читайте ниже.
  • $stopSymbols — массив со стоп-символов, которые отсеиваются на этапе контент-анализа текста. Этот список содержит все символы, которые могут помешать и являются лишними.

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

Применяемые стили

В виджете похожих статей применено всего пару CSS-стилей, которые вы можете по своему усмотрению использовать и дополнять. Поскольку у меня основная часть стилей берётся из стилей для всего блога, то мне достаточно этого (добавьте в style.css):

1
2
3
4
5
6
7
.related-posts {
margin:5px 0;
padding:0;
}
.related-posts .post-description{
font-size:.9em;
}

Где .related-posts отвечает за весь контейнер, в котором находятся похожие посты, а .post-description за отображение описательного сниппета для каждого поста. У себя вы можете изменить отображения на свой вкус, например, если понадобится применить другие стили для ссылок, то необходимо будет добавить стиль .related-posts a {}.

Вызов функции

Как вы могли заметить, функция принимает один аргумент: идентификатор поста. Там, где вы будете вызывать эту функцию, необходимо вручную получить и передать идентификатор. Допустим, что вызвать вы её хотите в шаблоне single.php после статьи. В этом случаем получить идентификатор можно с помощью функции get_the_ID():

1
2
<h2>Похожие статьи</h2>
<?php sp_related_posts(get_the_ID()); ?>

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

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

Стоп-слова

Как вы могли заметить, для отсеивания ненужных слов используется массив со стоп-словами, но в нем почему-то нет таких очевидных стоп-слов, как разные союзы, предлоги и др. Это объясняется тем, что минимальная длинна анализируемого слова (переменная $minLengthWord) равна 3 (по умолчанию). Из этого вытекает, то что всё, что короче 3х символов автоматически выбрасывается и потому в стоп-слова помещаются только то, что имеет в себе 3 или больше символов.

Если вы вдруг захотите уменьшить (или увеличить) минимальное количество символов в анализируемом слове, то вам придётся изменить и массив стоп-слов. Чтобы вам было легче, я предоставлю такой массив для условия, когда минимальное количество символов равно 1. И вы уже будете отталкиваться от него и удалять лишние слова (например если захотите использовать минимальное количество равное 2), тем более что он упорядочен по количеству символов в слове (удалять просто). В противном случае, если вы не удалите лишние, то алгоритм будет работать чуть дольше, чем он бы работал без этих слов, всё таки он это всё сравнивает (344 слова):

1
$stopWords = array (‘6’, ‘8’, ‘9’, ‘5’, ‘4’, ‘1’, ‘2’, ‘3’, ‘0’, ‘7’, ‘п’, ‘ж’, ‘н’, ‘м’, ‘р’, ‘т’, ‘ч’, ‘ц’, ‘х’, ‘ф’, ‘а’, ‘с’, ‘д’, ‘е’, ‘ё’, ‘г’, ‘ш’, ‘л’, ‘я’, ‘в’, ‘б’, ‘з’, ‘щ’, ‘ы’, ‘ю’, ‘у’, ‘к’, ‘ь’, ‘о’, ‘э’, ‘и’, ‘со’, ‘ну’, ‘бы’, ‘да’, ‘ни’, ‘до’, ‘об’, ‘же’, ‘за’, ‘мы’, ‘их’, ‘во’, ‘вы’, ‘уж’, ‘по’, ‘из’, ‘ли’, ‘от’, ‘то’, ‘но’, ‘им’, ‘на’, ‘ты’, ‘он’, ‘не’, ‘те’, ‘ко’, ‘её’, ‘ей’, ‘ее’, ‘они’, ‘том’, ‘тот’, ‘обо’, ‘она’, ‘три’, ‘без’, ‘ста’, ‘сто’, ‘них’, ‘там’, ‘тем’, ‘тут’, ‘оба’, ‘той’, ‘оно’, ‘под’, ‘сам’, ‘чьё’, ‘эта’, ‘эти’, ‘про’, ‘ней’, ‘это’, ‘чье’, ‘ото’, ‘ним’, ‘час’, ‘при’, ‘чей’, ‘чем’, ‘чья’, ‘что’, ‘уже’, ‘всю’, ‘ему’, ‘его’, ‘для’, ‘два’, ‘еще’, ‘ещё’, ‘изо’, ‘ибо’, ‘нет’, ‘год’, ‘где’, ‘вас’, ‘вам’, ‘был’, ‘вне’, ‘вот’, ‘вся’, ‘всё’, ‘все’, ‘или’, ‘эту’, ‘мой’, ‘мог’, ‘мне’, ‘так’, ‘над’, ‘нам’, ‘неё’, ‘нее’, ‘наш’, ‘нас’, ‘кто’, ‘мои’, ‘как’, ‘раз’, ‘моя’, ‘буду’, ‘этой’, ‘свое’, ‘тема’, ‘того’, ‘темы’, ‘тему’, ‘теме’, ‘твой’, ‘себе’, ‘этом’, ‘чуть’, ‘тебя’, ‘чтоб’, ‘куда’, ‘свою’, ‘чего’, ‘хотя’, ‘хоть’, ‘часу’, ‘этот’, ‘тоже’, ‘меня’, ‘могу’, ‘либо’, ‘ином’, ‘иной’, ‘надо’, ‘него’, ‘пять’, ‘сами’, ‘оный’, ‘один’, ‘ноль’, ‘есть’, ‘если’, ‘быть’, ‘ведь’, ‘было’, ‘были’, ‘была’, ‘весь’, ‘вниз’, ‘день’, ‘даже’, ‘всех’, ‘всей’, ‘свой’, ‘лишь’, ‘себя’, ‘семь’, ‘стал’, ‘из-за’, ‘кроме’, ‘можно’, ‘какой’, ‘через’, ‘таких’, ‘разве’, ‘когда’, ‘лучше’, ‘какое’, ‘потом’, ‘может’, ‘такою’, ‘такую’, ‘такие’, ‘перед’, ‘таком’, ‘также’, ‘между’, ‘вдоль’, ‘много’, ‘такое’, ‘точно’, ‘стали’, ‘стать’, ‘будем’, ‘давай’, ‘среди’, ‘всего’, ‘стала’, ‘видно’, ‘тогда’, ‘внизу’, ‘давно’, ‘сразу’, ‘здесь’, ‘знать’, ‘иметь’, ‘назад’, ‘зачем’, ‘таким’, ‘долго’, ‘такой’, ‘будут’, ‘такая’, ‘снова’, ‘тысяч’, ‘самим’, ‘опять’, ‘около’, ‘чтобы’, ‘после’, ‘самой’, ‘темою’, ‘будет’, ‘будто’, ‘редко’, ‘шесть’, ‘самая’, ‘часто’, ‘очень’, ‘темой’, ‘жизнь’, ‘самый’, ‘пусть’, ‘темам’, ‘вдруг’, ‘самым’, ‘этого’, ‘прямо’, ‘некто’, ‘никто’, ‘равно’, ‘какая’, ‘темах’, ‘почти’, ‘более’, ‘только’, ‘четыре’, ‘самому’, ‘десять’, ‘вместо’, ‘будете’, ‘теперь’, ‘девять’, ‘далеко’, ‘всегда’, ‘будешь’, ‘ребята’, ‘восемь’, ‘внутри’, ‘вокруг’, ‘давать’, ‘похоже’, ‘однако’, ‘иногда’, ‘такого’, ‘ничего’, ‘сказал’, ‘хорошо’, ‘нельзя’, ‘тысячу’, ‘тысячи’, ‘темами’, ‘тысяча’, ‘такому’, ‘больше’, ‘отчего’, ‘почему’, ‘потому’, ‘совсем’, ‘сейчас’, ‘другие’, ‘именно’, ‘другой’, ‘нибудь’, ‘каждым’, ‘такими’, ‘другое’, ‘каждый’, ‘большой’, ‘каждому’, ‘которые’, ‘конечно’, ‘человек’, ‘сначала’, ‘каждого’, ‘впрочем’, ‘кажется’, ‘никогда’, ‘сказали’, ‘сказать’, ‘недавно’, ‘сегодня’, ‘который’, ‘говорил’, ‘говорят’, ‘спасибо’, ‘говорим’, ‘другого’, ‘наконец’, ‘сказала’, ‘особенно’, ‘говорили’, ‘навсегда’, ‘говорить’, ‘которого’, ‘несколько’, ‘достаточно’, ‘пожалуйста’);

Так же может случится, что вы захотите использовать этот алгоритм для применения на англоязычных сайтах, где весь контент на английском. Для этого вам нужно сменить все стоп-слова на соответствующие. Чтобы вам было легче я предоставлю свой список английских стоп-слов для условия, что минимальная длинна анализируемого слова 1 символ (596 слов):

1
$stopWords = array (‘6’, ‘8’, ‘9’, ‘5’, ‘4’, ‘1’, ‘2’, ‘3’, ‘0’, ‘7’, ‘a’, ‘s’, ‘p’, ‘o’, ‘r’, ‘n’, ‘j’, ‘g’, ‘v’, ‘b’, ‘l’, ‘k’, ‘q’, ‘m’, ‘h’, ‘e’, ‘f’, ‘w’, ‘t’, ‘d’, ‘u’, ‘z’, ‘i’, ‘y’, ‘x’, ‘c’, ‘on’, ‘an’, ‘to’, ‘if’, ‘he’, ‘ie’, ‘be’, ‘et’, ‘de’, ‘am’, ‘th’, ‘or’, ‘id’, ‘im’, ‘ve’, ‘ok’, ‘oh’, ‘in’, ‘at’, ‘hi’, ‘as’, ‘vs’, ‘it’, ‘go’, ‘of’, ‘is’, ‘me’, ‘no’, ‘un’, ‘ex’, ‘up’, ‘rd’, ‘qv’, ‘us’, ‘do’, ‘ll’, ‘nd’, ‘co’, ‘by’, ‘we’, ‘re’, ‘my’, ‘so’, ‘eg’, ‘new’, ‘but’, ‘nor’, ‘not’, ‘que’, ‘cry’, ‘con’, ‘due’, ‘one’, ‘can’, ‘off’, ‘per’, ‘saw’, ‘old’, ‘yes’, ‘yet’, ‘why’, ‘see’, ‘say’, ‘any’, ‘put’, ‘our’, ‘ask’, ‘isn’, ‘com’, ‘top’, ‘are’, ‘non’, ‘you’, ‘own’, ‘ten’, ‘who’, ‘now’, ‘few’, ‘was’, ‘edu’, ‘she’, ‘etc’, ‘his’, ‘sup’, ‘six’, ‘may’, ‘ltd’, ‘let’, ‘viz’, ‘did’, ‘the’, ‘how’, ‘him’, ‘way’, ‘its’, ‘her’, ‘far’, ‘sub’, ‘has’, ‘too’, ‘use’, ‘via’, ‘all’, ‘and’, ‘had’, ‘got’, ‘two’, ‘for’, ‘inc’, ‘try’, ‘get’, ‘out’, ‘away’, ‘full’, ‘side’, ‘part’, ‘thin’, ‘show’, ‘back’, ‘fify’, ‘fire’, ‘move’, ‘call’, ‘bill’, ‘zero’, ‘best’, ‘well’, ‘each’, ‘also’, ‘even’, ‘want’, ‘else’, ‘find’, ‘hers’, ‘done’, ‘mill’, ‘made’, ‘mine’, ‘both’, ‘down’, ‘give’, ‘took’, ‘very’, ‘uses’, ‘this’, ‘gone’, ‘uucp’, ‘goes’, ‘hasn’, ‘hadn’, ‘wasn’, ‘went’, ‘shan’, ‘aren’, ‘didn’, ‘used’, ‘help’, ‘here’, ‘upon’, ‘unto’, ‘does’, ‘fill’, ‘ever’, ‘five’, ‘from’, ‘thus’, ‘thru’, ‘four’, ‘gets’, ‘have’, ‘been’, ‘into’, ‘says’, ‘name’, ‘much’, ‘look’, ‘know’, ‘must’, ‘near’, ‘like’, ‘self’, ‘they’, ‘mean’, ‘many’, ‘most’, ‘over’, ‘than’, ‘them’, ‘able’, ‘ours’, ‘then’, ‘take’, ‘plus’, ‘tell’, ‘once’, ‘onto’, ‘okay’, ‘that’, ‘were’, ‘only’, ‘more’, ‘ones’, ‘when’, ‘what’, ‘whom’, ‘will’, ‘need’, ‘last’, ‘wish’, ‘with’, ‘seem’, ‘your’, ‘come’, ‘cant’, ‘came’, ‘same’, ‘just’, ‘next’, ‘kept’, ‘sure’, ‘nine’, ‘seen’, ‘sent’, ‘lest’, ‘less’, ‘some’, ‘keep’, ‘none’, ‘said’, ‘such’, ‘soon’, ‘doing’, ‘often’, ‘eight’, ‘first’, ‘aside’, ‘below’, ‘inner’, ‘given’, ‘liked’, ‘hence’, ‘going’, ‘later’, ‘could’, ‘about’, ‘apart’, ‘needs’, ‘every’, ‘fifth’, ‘hello’, ‘might’, ‘alone’, ‘allow’, ‘comes’, ‘along’, ‘keeps’, ‘mustn’, ‘weren’, ‘among’, ‘doesn’, ‘haven’, ‘again’, ‘noone’, ‘forth’, ‘looks’, ‘after’, ‘brief’, ’cause’, ‘gives’, ‘known’, ‘knows’, ‘novel’, ‘other’, ‘tries’, ‘three’, ‘hasnt’, ‘thank’, ‘found’, ‘their’, ‘forty’, ‘seems’, ‘seven’, ‘until’, ‘right’, ‘front’, ‘those’, ‘sixty’, ‘think’, ‘these’, ‘third’, ’empty’, ‘there’, ‘tried’, ‘quite’, ‘above’, ‘thanx’, ‘which’, ‘thats’, ‘under’, ‘whose’, ‘twice’, ‘tends’, ‘where’, ‘ought’, ‘never’, ‘least’, ‘being’, ‘maybe’, ‘taken’, ‘would’, ‘shall’, ‘still’, ‘sorry’, ‘value’, ‘using’, ‘wants’, ‘yours’, ‘whole’, ‘while’, ‘truly’, ‘since’, ‘myself’, ‘saying’, ‘mostly’, ‘namely’, ‘nearly’, ‘second’, ‘always’, ‘little’, ‘mainly’, ‘really’, ‘likely’, ‘twenty’, ‘seeing’, ‘anyhow’, ‘selves’, ‘twelve’, ‘others’, ‘anyone’, ‘latter’, ‘almost’, ‘system’, ‘should’, ‘couldn’, ‘wouldn’, ‘itself’, ‘thickv’, ‘rather’, ‘thanks’, ‘seemed’, ‘placed’, ‘lately’, ‘please’, ‘anyway’, ‘inward’, ‘merely’, ‘either’, ‘asking’, ‘across’, ‘around’, ‘though’, ‘beside’, ‘within’, ‘wonder’, ‘course’, ‘detail’, ‘hardly’, ‘having’, ‘hither’, ‘hereby’, ‘toward’, ‘gotten’, ‘appear’, ‘thence’, ‘trying’, ‘nobody’, ‘theirs’, ’causes’, ‘herein’, ‘during’, ‘become’, ‘indeed’, ‘unless’, ‘theres’, ‘better’, ‘enough’, ‘allows’, ‘before’, ‘former’, ‘behind’, ‘bottom’, ‘cannot’, ‘beyond’, ‘amount’, ‘became’, ‘whence’, ‘useful’, ‘except’, ‘eleven’, ‘instead’, ‘ignored’, ‘insofar’, ‘somehow’, ‘towards’, ‘happens’, ‘thereby’, ‘anyways’, ‘example’, ‘however’, ‘therein’, ‘howbeit’, ‘sincere’, ‘getting’, ‘several’, ‘further’, ‘looking’, ‘serious’, ‘shouldn’, ‘someone’, ‘hundred’, ‘anybody’, ‘follows’, ‘awfully’, ‘exactly’, ‘because’, ‘various’, ‘becomes’, ‘seeming’, ‘whether’, ‘welcome’, ‘contain’, ‘outside’, ‘already’, ‘couldnt’, ‘without’, ‘nowhere’, ‘another’, ‘overall’, ‘perhaps’, ‘regards’, ‘against’, ‘amongst’, ‘despite’, ‘neither’, ‘fifteen’, ‘willing’, ‘wherein’, ‘herself’, ‘usually’, ‘certain’, ‘himself’, ‘whoever’, ‘through’, ‘specify’, ‘besides’, ‘between’, ‘whereby’, ‘clearly’, ‘changes’, ‘whereas’, ‘nothing’, ‘whither’, ‘believe’, ‘followed’, ‘whatever’, ‘whenever’, ‘somebody’, ‘probably’, ‘sometime’, ‘amoungst’, ‘everyone’, ‘consider’, ‘normally’, ‘inasmuch’, ‘entirely’, ‘provides’, ‘contains’, ‘wherever’, ‘possible’, ‘yourself’, ‘anywhere’, ‘formerly’, ‘sensible’, ‘describe’, ‘anything’, ‘interest’, ‘somewhat’, ‘becoming’, ‘together’, ‘although’, ‘indicate’, ‘actually’, ‘hereupon’, ‘secondly’, ‘thorough’, ‘latterly’, ‘moreover’, ‘unlikely’, ‘ourselves’, ‘following’, ‘whereupon’, ‘immediate’, ‘certainly’, ‘currently’, ‘hereafter’, ‘specified’, ‘described’, ‘greetings’, ‘available’, ‘indicates’, ‘therefore’, ‘indicated’, ‘hopefully’, ‘seriously’, ‘necessary’, ‘otherwise’, ‘somewhere’, ‘everybody’, ‘obviously’, ‘regarding’, ‘sometimes’, ‘meanwhile’, ‘elsewhere’, ‘downwards’, ‘different’, ‘something’, ‘according’, ‘thereupon’, ‘definitely’, ‘associated’, ‘everything’, ‘concerning’, ‘especially’, ‘whereafter’, ‘relatively’, ‘yourselves’, ‘beforehand’, ‘particular’, ‘containing’, ‘presumably’, ‘thoroughly’, ‘thereafter’, ‘everywhere’, ‘regardless’, ‘throughout’, ‘afterwards’, ‘reasonably’, ‘appreciate’, ‘specifying’, ‘themselves’, ‘furthermore’, ‘appropriate’, ‘considering’, ‘accordingly’, ‘particularly’, ‘respectively’, ‘consequently’, ‘nevertheless’, ‘corresponding’, ‘unfortunately’)

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

Примечание

Если у вас плохой хостинг, то функция может не сработать. Ведь для того, чтобы она работала нужно, чтобы работали функции mb_substr(), mb_strlen() и другие функции mb_. А они работают только если ваш PHP поддерживает библиотеку mbstring, которая предоставляет функции для работы с мультибайтовыми строками. Обычно, эта библиотека везде подключена, но ваш хостер может оказаться недобросовестным и по умолчанию у него она может быть выключена. Вы можете написать в поддержку и попросить её включить, думаю вам пойдут на встречу. А если вы сами имеете доступ к php.ini, то просто раскомментируйте или допишите (если нет) строку extension = php_mbstring.dll.

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

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

На этом у меня всё, если будут вопросы, пожелания или предложения, то прошу в комментарии. Рад буду обсудить своё поделие. Всем удачи!