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

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

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

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

 CD00123 мая 2012 г., 16:31
Вы можете попробовать посмотреть что-то вроде Lucene (keywordAnalyzer) ...
 Jeroen02 июн. 2012 г., 10:07
Это выглядит очень многообещающе, спасибо!
 Leblanc Meneses02 июн. 2012 г., 03:07
Для решения стоп-слов я использую базу данных wordnet. Я также использую этот элемент управления для визуализации Density + Relationship.codeproject.com/Articles/342715/…

Ответы на вопрос(5)

Решение Вопроса

что я сделал, как можно лучше.

Steps Filter text Split into words Remove 2 character words and stopwords Determine word frequency + density Determine word prominence Determine word containers Title Meta description URL Headings Meta keywords Calculate keyword value 1. Filter text

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

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

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

2. Split into words
$words = mb_split( ' +', $text );
3. Remove 2 character words and stopwords

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

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

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

4. Determine word frequency + density

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

$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. Determine word prominence

Выдача слова определяется положением слов в тексте. Например, второе слово в первом предложении, вероятно, более важно, чем 6-е слово в 83-м предложении.

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

$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. Determine word containers

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

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

7. Calculate keyword value

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

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

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

Conclusion

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

N.B. Yes, this was inspired by the today's blogpost about answering your own questions!

 30 мая 2012 г., 15:57
@Jeroen вы можете повысить скорость всего процесса, переместив самую интенсивную часть в расширение php.
 Jeroen23 мая 2012 г., 16:27
Примечание. Если у кого-то есть какие-либо идеи по поводу того, как его улучшить, вы можете отредактировать мой ответ или добавить другой ответ, и я хотел бы его услышать!
 26 мая 2012 г., 22:49
Это хороший алгоритм.
 30 мая 2012 г., 19:02
@Jeroen да, для этого потребуется C, потому что он обеспечивает значительный прирост скорости.
 Jeroen30 мая 2012 г., 18:10
@Vlad: Это будет означать написание всего кода на C, верно? Это определенно способ значительно ускорить его, но, к сожалению, в настоящее время мне не хватает опыта для этого.

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

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

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

load full page in detect language extract only <body> into seperate field release a tad of memory from <head> and others like, eg. unset($fullpage); fire your algorithm (if pcntl - linux host - is available, forking and releasing browser is a nice feature)

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

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

Если вы прокрутите немного вниз, посмотрите на предложенную схему - при первоначальном сканировании в базу данных будет помещено только тело (и дополнительно в вашем случае lang), а затем запустите 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);
    }

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

Опять же, что касается обработки базы данных, я закончил тем, что вставил вышеописанный результат вfull-text flagged tablecolumn так что будущие поиски пойдут без всяких проблем. Этоhuge advantage for db engines.

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

При работе с небольшим количеством документов механизм полнотекстового поиска может напрямую сканировать содержимое документов по каждому запросу, что называется стратегией последовательного сканирования. Это то, что делают некоторые элементарные инструменты, такие как 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`
)

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

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

 02 июн. 2012 г., 20:51
неправильный ответ - полнотекстовый очень полезен для поиска содержимого сайта (например, google :), поскольку он использует хэши, проиндексированные токенами вместо «последовательного подхода», который аналогиченgrep, На большом наборе документов последовательные поиски становятся длиннее. Сделайте замечание о «невесомости» его остаток от моего собственного алгоритма, где я не ставлю веса на ключевые слова и где я летаю только с одним столбцом
 Jeroen02 июн. 2012 г., 20:59
Хорошо, это объясняет, спасибо! (хотя мне это не нужно, я просто сохраняю список ключевых слов)
 Jeroen02 июн. 2012 г., 20:33
full-text на самом деле это одно из слов, которые я понял, я работал с MySQL раньше, но что вы подразумеваете подflagged а такжеweightless?
 Jeroen02 июн. 2012 г., 17:29
Спасибо за эти замечательные предложения! Я скоро начну переписывать свой код на основе предоставленного вами кода (вероятно, поместит его на GitHub). Но одно дело, что именно вы подразумеваете подa full-text flagged table (weightless) field?
 02 июн. 2012 г., 20:19
это варьируется от базы данных к базе данных, что означает слово «полный текст»; средства. Лично я работаю только с MySQL. Здесь вам нужно будет создать таблицу (или изменить одну) для использования MyISAM, а затем установить индекс для вашего столбца. Можно использовать только столбцы CHAR, VARCHAR или TEXT, что вполне понятно, не правда ли? :) Проверьте:dev.mysql.com/doc/refman/5.0/en/fulltext-search.html имея в виду предостережения:dev.mysql.com/doc/refman/5.0/en/fulltext-restrictions.html

чтобы заново изобретать колесо, вы используете Apache SoIr для поиска и анализа. Он имеет почти все, что вам может понадобиться, включая обнаружение стоп-слов для более 30 языков [насколько я помню, может быть даже больше] и делает тонны вещей с данными, хранящимися в нем.

 Jeroen31 мая 2012 г., 15:20
Я не создаю функцию поиска, я использую ее для отображения анализа ключевых слов на веб-сайте; насколько я знаю, Solr / Lucene не в состоянии сделать это ..

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

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

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

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

 Jeroen31 мая 2012 г., 22:21
Хотя это слишком сложный способ применения анализа ключевых слов, это отличные предложения о том, как его улучшить, спасибо!
 Jeroen31 мая 2012 г., 22:53
Однако, если кто-то использует это для анализа чего-то вроде статей / сообщений в блоге, это определенно хорошая идея!
 Jeroen31 мая 2012 г., 22:52
Я попробовал это, используя проект Readability (keyvan.net/2010/08/php-readability), но иногда он получит неправильный блок текста. Кроме того, я анализирую в основном страницы веб-сайтов, поэтому на них часто нет основного текстового блока.
 31 мая 2012 г., 22:50
@Jeroen, кстати, отфильтровывая HTML-теги на самом первом шаге, можно отбросить важную информацию о структуре документа. Я предлагаю сначала проанализировать документ как html-документ, определить его основной блок контента и только потом применить ваш алгоритм к основному контенту. Это позволит вам исключить из рассмотрения меню, формы, колонтитулы и все вспомогательные элементы.

Context scoring

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

Frequency sanitization

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

 02 июн. 2012 г., 13:01
тогда что он будет делать с документами, которые содержат только 1 плотность? :)

Ваш ответ на вопрос