Analyse de mots clés en PHP

pour une application web que je construis je dois analyser un site web, extraire et classer ses mots clés les plus importants et les afficher.

obtenir tous les mots, leur densité et afficher ceux-ci est relativement simple, mais cela donne des résultats très biaisés (par exemple, les mots d'arrêt se classant très haut).

fondamentalement, ma question Est: Comment puis-je créer un outil d'analyse de mots clés en PHP qui aboutit à une liste correctement ordonnée par l'importance des mots?

23
demandé sur mschr 2012-05-23 18:16:56

5 réponses

Récemment, j'ai travaillé sur moi-même, et je vais essayer d'expliquer ce que j'ai fait du mieux que possible.

Étapes

  1. texte du filtre
  2. divisé en mots
  3. supprimer les mots à deux caractères et les mots d'arrêt
  4. déterminer la fréquence des mots + la densité
  5. déterminer la proéminence du mot
  6. déterminer les conteneurs word
    1. titre
    2. Meta description
    3. URL
    4. rubriques
    5. Meta keywords
  7. calculer la valeur des mots clés

1. Texte du filtre

la première chose que vous devez faire est de filtrer s'assurer que l'encodage est correct, donc convertir est à UTF-8:

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

après ça, vous devez supprimer toutes les balises html, la ponctuation, les symboles et les numéros. Cherchez les fonctions sur la façon de faire cela sur Google!

2. Divisé en mots

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

3. Supprimer les mots de deux caractères et les mots d'arrêt

tout mot composé de 1 ou 2 caractères ne sera d'aucune importance, donc nous les supprimons tous.

pour supprimer les mots d'arrêt, nous devons d'abord détecter la langue. Il ya un couple de façons de le peut ce faire: - Vérification de L'en-tête HTTP Content-Language - Vérification de l'attribut lang="" ou xml:lang="" - Vérification des balises de métadonnées de langue et de contenu-langue Si aucun de ces paramètres n'est défini, vous pouvez utiliser une API externe comme AlchemyAPI .

vous aurez besoin d'une liste de mots d'arrêt par langue, qui peut être facilement trouvé sur le web. J'ai utilisé celui-ci: http://www.ranks.nl/resources/stopwords.html

4. Déterminer la fréquence des mots + la densité

pour compter le nombre d'occurrences par mot, utilisez ceci:

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

maintenant boucle à travers le tableau $ uniqueWords et calculer la densité de chaque mot comme ceci:

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

5. Déterminer la proéminence des mots

le mot proéminence est défini par la position des mots dans le texte. Par exemple, le premier mot de la première phrase est probablement plus important que le 6ème mot de la 83ème phrase.

pour le calculer, ajouter ce code dans la même boucle de l'étape précédente:

$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. Déterminer le mot conteneurs

une partie très importante est de déterminer où un mot réside - dans le titre, la description et plus.

tout d'abord, vous devez saisir le titre, toutes les balises de métadonnées et toutes les en-têtes en utilisant quelque chose comme DOMDocument ou PHPQuery ( ne essayez d'utiliser la regex!) Ensuite, vous devez vérifier, dans la même boucle, si ceux-ci contiennent les mots.

7. Calculer la valeur du mot-clé

la dernière étape est de calculer une valeur de mots clés. Pour ce faire, vous devez peser chaque facteur - densité, proéminence et conteneurs. Par exemple:

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

Ce calcul est loin d'être parfait, mais il devrait vous donner des résultats décents.

Conclusion

je n'ai pas mentionné tous les détails de ce que j'ai utilisé dans mon outil, mais j'espère qu'il offre une bonne vue dans l'analyse des mots clés.

N.B. Oui, cela a été inspiré par le blogpost d'aujourd'hui sur Répondre à vos propres questions!

49
répondu Jeroen 2012-05-31 16:57:29

une chose qui manque dans votre algorithme est l'analyse documentaire (si vous ne l'avez pas omise intentionnellement pour une raison quelconque).

Chaque site est construit sur un ensemble de documents. Compter les fréquences de mots pour tous et chaque document vous fournira des informations sur la couverture des mots. Les mots qui apparaissent dans la plupart des documents sont des mots d'arrêt. Les mots spécifiques pour un nombre limité de documents peuvent former un groupe de documents sur un sujet précis. Nombre de les documents relatifs à un sujet spécifique peut augmenter l'importance de la parole du sujet, ou au moins fournir un facteur supplémentaire d'être comptés dans vos formules.

peut-être, vous pourriez bénéficier d'un classificateur préconfiguré qui contient des catégories/sujets et des mots-clés pour chacun d'eux (cette tâche peut être partiellement automatisée en indexant les hiérarchies publiques existantes de catégories, jusqu'à Wikipédia, mais ce n'est pas une tâche triviale en soi). Alors vous pouvez impliquer catégories d'analyses.

aussi, vous pouvez améliorer les statistiques par l'analyse au niveau de la phrase. C'est-à-dire, ayant des fréquences de combien de mots se produisent dans la même phrase ou phrase, vous pouvez découvrir des clichés et des doublons et les éliminer des statistiques. Mais, je crains que cela ne soit pas facilement impémenté en PHP pur.

4
répondu Stan 2012-05-31 20:03:20

c'est probablement une petite contribution, mais je vais la mentionner quand même.

Contexte de notation

dans une certaine mesure, vous regardez déjà le contexte d'un mot en utilisant la position dans laquelle il est placé. Vous pouvez ajouter un autre facteur à cela en classant les mots qui apparaissent dans une vedette (H1, H2, etc.) plus élevé que les mots à l'intérieur d'un paragraphe, plus élevé que peut-être les mots dans une liste à puces, etc.

fréquence assainissement

détecter les mots d'arrêt basés sur une langue pourrait fonctionner, mais peut-être pourriez-vous envisager d'utiliser une courbe en cloche pour déterminer quelles fréquences / densités de mots sont trop extravagantes (par exemple, bande inférieure de 5% et supérieure de 95%). Ensuite, appliquez le pointage sur les mots restants. Non seulement il empêche les mots stop, mais aussi l'abus de mots clés, au moins en théorie:)

4
répondu Ja͢ck 2012-06-01 14:28:03

@ raffinage 'Étapes'

en ce qui concerne la réalisation de ces nombreuses étapes, j'irai avec une solution un peu "améliorée", suturant certaines de vos étapes ensemble.

pas sûr, si un lexer complet est mieux cependant, si vous le concevez parfaitement pour répondre à vos besoins, par exemple regarder seulement pour le texte dans hX etc. Mais vous devriez signifier _serious business car il peut être un casse-tête à mettre en œuvre. Bien que je vais mettre mon point de vue et dire qu'une solution Flex / Bison dans une autre langue (PHP offre un mauvais support car C'est un langage de haut niveau) serait un boost de vitesse "insensé".

cependant, heureusement libxml fournit des traits magnifiques et comme ce qui suit devrait montrer, vous finirez par avoir plusieurs étapes dans un. Avant le point où vous analysez le contenu, setup language (stopwords), minify le jeu de NodeList et de travailler à partir de là.

  1. "
  2. détecter la langue
  3. extraire seulement <body> dans un champ séparé
  4. libérer un peu de mémoire de <head> et d'autres comme, par exemple. unset($fullpage);
  5. lancez votre algorithme (si pcntl - Linux host - est disponible, bifurquer et libérer le navigateur est une fonctionnalité agréable)

en utilisant DOM parsers, on devrait réalisez que les paramètres peuvent introduire une validation supplémentaire pour les attributs HREF et src, en fonction de la bibliothèque (comme parse_url et likes)

une autre façon d'éviter les problèmes de consommation de temps et de mémoire est d'appeler php-cli (qui fonctionne aussi pour un hôte windows) et "Get on with business" et de commencer le document suivant. Voir cette question pour plus d'informations.

si vous faites un peu défiler vers le bas, regardez le schéma proposé - rampement initial mettrait seulement le corps dans la base de données (et plus lang dans votre cas) et ensuite lancer un script cron, en remplissant le ft_index tout en utilisant la fonction suivante

    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);
    }

ce qui précède est une classe, ressemblant à votre base de données qui a le champ 'body' de la page chargé dans prehand.

encore une fois, en ce qui concerne le traitement de la base de données va, j'ai fini par insérer le résultat analysé ci-dessus dans un tablecolumn plein texte flagged de sorte que le futur les recherches irait sans faille. Il s'agit d'un énorme avantage pour les moteurs db .

note sur l'indexation en texte intégral:

lorsqu'il s'agit d'un petit nombre de documents, il est possible pour le moteur de recherche plein texte de numériser directement le contenu des documents avec chaque requête, une stratégie appelée balayage en série. C'est ce que font certains outils rudimentaires, comme grep, lors d'une recherche.

votre algorithme d'indexation les filtres de quelques mots, ok.. Mais ceux - ci sont énumérés par combien de poids ils portent-Il ya une stratégie à penser ici, car une chaîne de caractères plein texte ne porte pas sur les poids donnés. C'est pourquoi dans l'exemple, comme stratégie de base sur la division des cordes en 3 cordes différentes est donnée.

une fois mis dans la base de données, les colonnes devraient alors ressembler à ceci, de sorte qu'un schéma pourrait être comme ainsi, où nous maintiendrions des poids - et encore offrir une méthode de requête très rapide

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;

on peut ajouter un index comme ceci:

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

la chose à propos de la détection de la langue et de sélectionner ensuite votre base de données de mots croisés à partir de ce point est une fonctionnalité que j'ai moi - même laissé de côté, mais son nifty-et par le livre! Donc cudos pour vos efforts et cette réponse:)

aussi, gardez à l'esprit qu'il n'y a pas seulement l'étiquette de titre, mais aussi les attributs de titre d'ancre / img. Si, pour une raison ou pour une autre, votre analyse spider-like state , je suggérerais de combiner le lien de référence ( <a> ) title et textContent avec la page cible <title>

4
répondu mschr 2017-05-23 11:54:30

au lieu de réinventer la roue, je vous conseille D'Utiliser Apache SoIr pour la recherche et l'analyse. Il a presque tout ce dont vous pourriez avoir besoin, y compris la détection de mots d'arrêt pour plus de 30 langues [autant que je me souvienne, pourrait être encore plus] et faire des tonnes de choses avec des données stockées dans elle.

2
répondu Shahriyar Imanov 2012-05-31 13:09:12