Анализ ключевых слов в PHP
для веб-приложения, которое я создаю, мне нужно проанализировать веб-сайт, получить и ранжировать его самые важные ключевые слова и отобразить их.
получение всех слов, их плотность и отображение относительно просты, но это дает очень искаженные результаты (например, рейтинг стоп-слов очень высокий).
в принципе, мой вопрос: Как я могу создать инструмент анализа ключевых слов в PHP, который приводит к списку, правильно упорядоченному по важности слова?
5 ответов
в последнее время, я работаю над этим сам, и я постараюсь объяснить, что я делал как можно лучше.
шаги
- текстовый фильтр
- разделить на слова
- удалить 2 символьных слова и стоп-слова
- определить частоту слов + плотность
- определить слово протуберанец
- определить слово контейнеров
- заголовок
- мета описание
- URL-адресом
- заголовки
- ключевые слова
- вычислить значение ключевого слова
1. Фильтровать текст
первое, что вам нужно сделать, это фильтр убедитесь, что кодировка верна, поэтому конвертировать в UTF-8:
iconv ($encoding, "utf-8", $file); // where $encoding is the current encoding
после этого вам нужно удалить все HTML-теги, знаки препинания, символы и цифры. Ищите функции о том, как это сделать в Google!
2. Расщеплять в слова!--39-->
$words = mb_split( ' +', $text );
3. Удалите 2 символьных слова и стоп-слова
$words = mb_split( ' +', $text );
любое слово, состоящее из 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. Определить слово протуберанец
слово выдающийся определяется положением слов в тексте. Например, второе слово в первом предложении, вероятно, более важно, чем 6-е слово в 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. Определите контейнеры word
очень важно, чтобы определить, где слово проживает-в названии, описании и многое другое.
во-первых, вам нужно захватить заголовок, все теги метаданных и все заголовки, используя что-то вроде DOMDocument или PHPQuery (не попробуйте использовать regex!) Затем вам нужно проверить, в том же цикле, содержат ли они слова.
7. Вычислить значение ключевого слова
последний шаг-вычислить значение ключевых слов. Для этого нужно взвесить каждый фактор-плотность, выступ и контейнеры. Для пример:
$value = (double) ((1 + $density) * ($prominence / 10)) * (1 + (0.5 * count ($containers)));
этот расчет далек от совершенства, но он должен дать вам достойные результаты.
вывод
Я не упомянул каждую деталь того, что я использовал в своем инструменте, но я надеюсь, что он предлагает хороший обзор анализа ключевых слов.
N. B. Да, это было вдохновлено сегодняшним блогпостом об ответах на ваши собственные вопросы!
одна вещь, которая отсутствует в вашем алгоритме,-это анализ, ориентированный на документ (если вы не опустили его намеренно по какой-то причине).
каждый сайт построен на наборе документов. Подсчет частот слов для всех и каждого документа предоставит вам информацию о покрытии слов. Слова, которые встречаются в большинстве документов являются стоп-слова. Слова, характерные для ограниченного числа документов, могут образовывать кластер документов по определенной теме. Количество документов отношение к конкретной теме может повысить общую важность слов темы или, по крайней мере, предоставить дополнительный фактор, который будет учитываться в ваших формулах.
возможно, вы могли бы воспользоваться предварительно настроенным классификатором, который содержит категории / темы и ключевые слова для каждого из них (эта задача может быть частично автоматизирована путем индексирования существующих публичных иерархий категорий, вплоть до Википедии, но это не тривиальная задача). Затем вы можете включить категории в analisys.
кроме того, вы можете улучшить статистику путем анализа на уровне предложения. То есть, имея частоты того, как часто слова встречаются в одном предложении или фразе, вы можете обнаружить клише и повторы, и исключить их из статистики. Но, боюсь, это не так легко сделать в чистом PHP.
Это, вероятно, небольшой вклад, но я все равно упомяну об этом.
контексте скоринга
в определенной степени вы уже смотрите на контекст слова, используя позицию, в которой он помещен. Вы можете добавить еще один фактор к этому, ранжируя слова, которые появляются в заголовке (H1, H2 и т. д.) выше, чем слова внутри абзаца, выше, чем возможно, слова в маркированном списке и т. д.
частота очистки
обнаружение стоп слова, основанные на языке, могут работать, но, возможно, вы могли бы использовать кривую колокола, чтобы определить, какие частоты / плотности слов слишком экстравагантны (например, полоса ниже 5% и верхняя 95%). Затем нанесите скоринг на оставшиеся слова. Это не только предотвращает стоп-слова, но и злоупотребление ключевым словом, по крайней мере в теории:)
@ нефтепереработки 'шаги'
что касается выполнения этих многих шагов, я бы пошел с немного "улучшенным" решением, сшивая некоторые из ваших шагов вместе.
не уверен, полный ли лексер лучше, если вы создадите его идеально, чтобы соответствовать вашим потребностям, например, искать только текст в hX и т. д. Но вы должны иметь в виду _serious бизнес, так как это может быть головной болью для реализации. Хотя я поставлю свою точку зрения и скажу, что a Flex / Bison решение на другом языке (PHP предлагает плохую поддержку, поскольку это такой язык высокого уровня) было бы "безумным" ускорением скорости.
к счастьюlibxml
предоставляет великолепные функции, и, как показано ниже, вы в конечном итоге будете иметь несколько шагов в одном. Перед тем, как вы проанализируете содержимое, установите язык (стоп-слова),минимизировать набор NodeList и работать оттуда.
- загрузить всю страницу в
- обнаружить язык
- извлечь только
<body>
в отдельном поле - отпустите немного памяти из
<head>
и другие, как, например.unset($fullpage);
- Запустите свой алгоритм (если pcntl - Linux host - доступен, разветвление и выпуск браузера-хорошая функция)
при использовании парсеров 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);
}
выше приведен класс, напоминающий вашу базу данных, которая имеет поле страницы "тело", загруженное в prehand.
опять же, что касается обработки базы данных, я закончил вставкой выше проанализированного результата в полнотекстовый помеченный tablecolumn так что будущие запросы будут идти успешно. Это огромное преимущество для двигателей DB.
примечание по полнотекстовой индексации:
имея дело с небольшим количеством документов для полнотекстового поиска, чтобы проверить содержимое документов с каждым запросом, стратегия последовательного сканирования. Это то, что некоторые рудиментарные инструменты, такие как grep, делают при поиске.
ваш алгоритм индексирования отфильтровывает некоторые слова, хорошо.. Но они перечислены по тому, сколько веса они несут - здесь есть стратегия, чтобы думать, так как полнотекстовая строка не переносит данные веса. Вот почему в примере, как basic приведена стратегия разбиения строк на 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`
)
вещь об обнаружении языка, а затем выборе базы данных стоп-слов с этого момента-это функция, которую я сам опустил, но ее изящный - и по книге! Так cudos за ваши усилия и этот ответ :)
кроме того, имейте в виду, что есть не только тег заголовка, но и атрибуты заголовка anchor / img. Если по какой-то причине ваша аналитика переходит в паук-как состояние, я бы предложил объединить ссылку ссылки (<a>
) title и textContent с целевой страницы <title>
Я бы рекомендовал вместо повторного изобретения колеса использовать Apache SoIr для поиска и анализа. В нем есть почти все что вам может понадобиться, включая стоп-слово определение для 30 языков [насколько я помню, может быть даже больше и делать тонны материала с данными, хранящимися в нем.