Анализ ключевых слов в PHP

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

Получение всех слов, их плотность и отображение их относительно просты, но это дает очень искаженные результаты (например, рейтинг стоп-слов очень высок).

В основном, мой вопрос: как я могу создать инструмент анализа ключевых слов в PHP, который приводит к списку, правильно упорядоченному по значению слова?

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

    меры

    1. Фильтровать текст
    2. Разделить слова
    3. Удалить 2 символьных слова и задержек
    4. Определить частоту слов + плотность
    5. Определите известность слова
    6. Определить контейнеры слов
      1. заглавие
      2. Мета-описание
      3. URL
      4. Заголовки
      5. Мета-ключевые слова
    7. Вычислить значение ключевого слова

    1. Фильтровать текст

    Первое, что вам нужно сделать, это фильтр, убедитесь, что кодировка верна, поэтому конвертировать в UTF-8:

    iconv ($encoding, "utf-8", $file); // where $encoding is the current encoding 

    После этого вам нужно снять все теги html, знаки препинания, символы и цифры. Посмотрите на функции, как это сделать в Google!

    2. Разделить слова

     $words = mb_split( ' +', $text ); 

    3. Удалите 2 символьных слова и стоп-слова.

    Любое слово, состоящее из 1 или 2 символов, не будет иметь никакого значения, поэтому мы удалим все из них.

    Чтобы удалить стоп-слова, нам сначала нужно определить язык. Есть несколько способов, которыми мы можем это сделать: – Проверка HTTP-заголовка Content-Language – Проверка атрибута lang = "" или xml: lang = "" – Проверка тегов метаданных языка и контента-языка Если ни один из них не установлен, вы можете использовать внешний API, такой как AlchemyAPI .

    Вам понадобится список стоп-слов на язык, который можно легко найти в Интернете. Я использовал этот: http://www.ranks.nl/resources/stopwords.html

    4. Определите частоту слов + плотность

    Чтобы подсчитать количество вхождений в слово, используйте следующее:

     $uniqueWords = array_unique ($keywords); // $keywords is the $words array after being filtered as mentioned in step 3 $uniqueWordCounts = array_count_values ( $words ); 

    Теперь проведите цикл $ uniqueWords и вычислите плотность каждого слова следующим образом:

     $density = $frequency / count ($words) * 100; 

    5. Определите известность слова

    Слово протуберанца определяется положением слов в тексте. Например, второе слово в первом предложении, вероятно, более важно, чем шестое слово в 83-м предложении.

    Чтобы вычислить его, добавьте этот код в тот же цикл из предыдущего шага:

     $keys = array_keys ($words, $word); // $word is the word we're currently at in the loop $positionSum = array_sum ($keys) + count ($keys); $prominence = (count ($words) - (($positionSum - 1) / count ($keys))) * (100 / count ($words)); 

    6. Определите контейнеры слов

    Очень важная часть – определить, где находится слово – в названии, описании и многом другом.

    Во-первых, вам нужно захватить заголовок, все теги метаданных и все заголовки, используя что-то вроде DOMDocument или PHPQuery ( не пытайтесь использовать регулярное выражение!). Затем вам нужно проверить, в том же цикле, содержат ли они слова.

    7. Вычислить значение ключевого слова

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

     $value = (double) ((1 + $density) * ($prominence / 10)) * (1 + (0.5 * count ($containers))); 

    Этот расчет далек от совершенства, но он должен дать вам достойные результаты.

    Вывод

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

    NB Да, это было вдохновлено сегодняшним blogpost о том, как отвечать на ваши вопросы!

    Одна вещь, которая отсутствует в вашем алгоритме, – это документально-ориентированный анализ (если вы не упустили его намеренно по какой-то причине).

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

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

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

    Это, вероятно, небольшой вклад, но, тем не менее, я упомянул об этом.

    Контекстная оценка

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

    Частотная дезинфекция

    Обнаружение стоп-слов на основе языка может работать, но, возможно, вы могли бы использовать кривую колокола, чтобы определить, какие частоты / плотности слов слишком экстравагантны (например, полоса ниже 5% и верхняя 95%). Затем примените оценку остальных слов. Это не только предотвращает стоп-слова, но и злоупотребляет ключевыми словами, по крайней мере, в теории 🙂

    @ уточнение «Шаги»

    Что касается выполнения этих многочисленных шагов, я бы пошел с немного расширенным решением, скрепляя некоторые из ваших шагов вместе.

    Не уверен, но лучше ли полноценный лексер , если вы прекрасно его спланируете в соответствии с вашими потребностями, например, смотрите только текст в формате hX и т. Д. Но вы должны иметь в виду бизнес-бизнес, поскольку он может стать головной болью для реализации. Хотя я остановлюсь и скажу, что решение Flex / Bison на другом языке (PHP предлагает низкую поддержку, поскольку это такой язык высокого уровня) будет «безумным» ускорением.

    Однако, к счастью, libxml предоставляет великолепные функции, и, как показано ниже, вы будете иметь несколько шагов в одном. Перед тем, как вы анализируете содержимое, язык установки (стоп-слова), уменьшите набор NodeList и работайте там.

    1. загрузить полную страницу в
    2. определить язык
    3. извлекать только <body> в отдельное поле
    4. освободить немного памяти от <head> и других, например, например. unset($fullpage); с unset($fullpage);
    5. запускайте свой алгоритм (если доступен pcntl – linux host, разворачивание и отключение браузера – хорошая функция)

    При использовании парсеров DOM следует понимать, что настройки могут вводить дополнительную проверку атрибутов href и src, в зависимости от библиотеки (например, parse_url и нравится)

    Еще один способ получить от тайм-аута / потребления памяти – вызвать php-cli (также работает для хоста Windows) и «заняться бизнесом» и запустить следующий документ. См. Этот вопрос для получения дополнительной информации.

    Если вы немного прокрутите список, посмотрите на предлагаемую схему – первоначальный обход поместил бы только тело в базу данных (и, кроме того, занесен в ваш случай), а затем запустил cron-скрипт, заполнив ft_index, используя следующую функцию

      function analyse() { ob_start(); // dont care about warnings, clean ob contents after parse $doc->loadHTML("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\"/></head><body><pre>" . $this->html_entity_decode("UTF-8") . "</pre></body>"); ob_end_clean(); $weighted_ft = array('0'=>"",'5'=>"",'15'=>""); $includes = $doc->getElementsByTagName('h1'); // relevance wieght 0 foreach ($includes as $h) { $text = $h->textContent; // check/filter stopwords and uniqueness // do so with other weights as well, basically narrow it down before counting $weighted_ft['0'] .= " " . $text; } // relevance wieght 5 $includes = $doc->getElementsByTagName('h2'); foreach ($includes as $h) { $weighted_ft['5'] .= " " . $h->textContent; } // relevance wieght 15 $includes = $doc->getElementsByTagName('p'); foreach ($includes as $p) { $weighted_ft['15'] .= " " . $p->textContent; } // pseudo; start counting frequencies and stuff // foreach weighted_ft sz do // foreach word in sz do // freqency / prominence } function html_entity_decode($toEncoding) { $encoding = mb_detect_encoding($this->body, "ASCII,JIS,UTF-8,ISO-8859-1,ISO-8859-15,EUC-JP,SJIS"); $body = mb_convert_encoding($this->body, $toEncoding, ($encoding != "" ? $encoding : "auto")); return html_entity_decode($body, ENT_QUOTES, $toEncoding); } 

    Вышеупомянутый класс, похожий на вашу базу данных, которая имеет поле «тело» страницы, загруженное в prehand.

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

    Примечание по полнотекстовому индексированию:

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

    Ваш алгоритм индексирования отфильтровывает некоторые слова, хорошо. Но они перечислены тем, сколько веса они несут – есть стратегия, чтобы придумать здесь, поскольку полнотекстовая строка не переносит приведенные веса. Вот почему в этом примере дается базовая стратегия по разбиению строк на 3 разные строки.

    После ввода в базу данных столбцы должны тогда напоминать это, поэтому схема может быть такой, где мы будем поддерживать весовые коэффициенты – и все же предлагаем метод сверхбыстрого запроса

     CREATE TABLE IF NOT EXISTS `oo_pages` ( `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, `body` mediumtext COLLATE utf8_danish_ci NOT NULL COMMENT 'PageBody entity encoded html', `title` varchar(31) COLLATE utf8_danish_ci NOT NULL, `ft_index5` mediumtext COLLATE utf8_danish_ci NOT NULL COMMENT 'Regenerated cron-wise, weighted highest', `ft_index10` mediumtext COLLATE utf8_danish_ci NOT NULL COMMENT 'Regenerated cron-wise, weighted medium', `ft_index15` mediumtext COLLATE utf8_danish_ci NOT NULL COMMENT 'Regenerated cron-wise, weighted lesser', `ft_lastmodified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'last cron run', PRIMARY KEY (`id`), UNIQUE KEY `alias` (`alias`), FULLTEXT KEY `ft_index5` (`ft_index5`), FULLTEXT KEY `ft_index10` (`ft_index10`), FULLTEXT KEY `ft_index15` (`ft_index15`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_danish_ci; 

    Можно добавить такой индекс:

     ALTER TABLE `oo_pages` ADD FULLTEXT ( `named_column` ) 

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

    Кроме того, имейте в виду, что есть не только тег title, но и атрибуты заголовка anchor / img. Если по какой-то причине ваша аналитика переходит в состояние , подобное пауку , я бы предложил объединить ссылку ссылки ( <a> ) title и textContent с целевой страницей <title>

    Я бы рекомендовал вместо того, чтобы изобретать колесо, вы используете Apache SoIr для поиска и анализа. У него есть почти все, что вам может понадобиться, в том числе обнаружение стоп-слова для 30 + языков (насколько я помню, возможно, еще больше) и делать множество вещей с данными, хранящимися в нем.