Partager la publication "Recherche de facteurs SEO avec le Machine Learning (partie 3)"
Dans cet article nous allons nous consacrer au contenu et au contenu vs les mots clés ou expressions cibles.
Remarque : cet article est le troisième consacré à la recherche de facteurs SEO à partir du Machine Learning.
Dans un premier article nous avions récupéré des données de positionnement de Pages dans les SERPs de Google au moyen de l’API de Yooda Insight.
Puis ensuite, dans un second article, nous avions enrichi ces données avec des données techniques propres aux pages.
A chaque fois, nous avions utilisé ces données pour rechercher des facteurs SEO potentiels au moyen d’algorithme de marchine learning comme par exemple glm, naïve Bayes, random Forest ou XGBoost.
Cette fois nous allons enrichir les données avec des facteurs en rapport avec le contenu et le contenu des pages vs les mots clés sur lesquels se positionnent les pages.
De quoi aurons nous besoin ?
Logiciel R
Comme précédemment, merci de télécharger Le Logiciel R sur ce site https://cran.r-project.org/, ainsi que l’environnement de développement RStudio ici : https://www.rstudio.com/products/rstudio/download/, afin de pouvoir tester vous même le code source.
Fichiers précédents
Vous aurez aussi besoin du fichier de positionnement avec données techniques .csv sauvegardé précédemment. il contient 149355 observations. Vous pouvez le récupérer sous forme compressé .zip ici : YoodaTechDataKeywords.zip.
Ainsi que les répertoires et les fichiers de pages html créé précédemment et qui sont disponibles dans notre boutique à cette adresse : https://www.anakeyn.com/boutique/produit/script-r-facteurs-seo-et-ml-3/ téléchargez les fichiers sources et les fichiers au format .7z (Seven-Zip) et installez-les dans le même répertoire que votre projet R.
Récupération du contenu HTML
Dans cette partie nous allons récupérer le contenu des pages html pour créer des variables en rapport avec les balises (title, description, keywords, H1…, H6, strong, b, p, body) et le contenu ou non des mots clés cibles dans ces balises. par exemple voici quelques variables :
- title.size : taille de la balise titre en caractères.
- title.keyword.count : nombre de fois ou l’expression recherchée est trouvé titre.
- title.clean.keywordsSplit.count : nombre de fois ou les mots clés contenu dans l’expression recherchée sont trouvés dans le titre.
- allBody.clean.keyword.frequency : fréquence d’apparition de l’expression recherchée dans la page.
- …
Code Source
Vous pouvez copier/coller les morceaux de code source dans un script R pour les tester.
Vous pouvez aussi récupérer le code source gratuitement dans notre boutique à l’adresse : https://www.anakeyn.com/boutique/produit/script-r-facteurs-seo-et-ml-3/
Chargement des bibliothèques
Attention ! si vous n’avez pas installé certains packages dans votre environnement RStudio, vous devez dé-commenter ceux qui vous intéressent.
#
#
#load(".RData")
########################################################################
#### Recherche de facteurs SEO au moyen du machine Learning partie 3
########################################################################
#Dans cette 3 eme partie nous allons enrichir notre jeu de données avec des informations en rapport avec le contenu des
#pages html et des informations en fonction des Mots clés vs contenus.
#le contenu des pages est récupéré à partir des fichiers de pages html créées précédemment et qui se trouvent dans des
#répertoires par domaines
#### Chargement des bibliothèques utiles ##########################################
#Installer une fois
#install.packages("plyr") #une fois
#install.packages("XML") #une fois
#install.packages("xml2")
#install.packages("textclean")
#install.packages("stringr")
#install.packages("RCurl")
#install.packages("rvest")
#install.packages("stringr") #une fois
#install.packages("vtreat") #une fois
#install.packages("magrittr") #une fois
#install.packages("xgboost") #une fois
#install.packages("dplyr") #une fois
#install.packages("pROC") #une fois
#install.packages("qdap") #une fois
#install.packages("qdapTools") #une fois
#install.packages("tm") #une fois
#install.packages("ggplot2") #une fois
#install.packages("caret") #une fois
#install.packages("installr") #une fois
#Charger les bibliothèques
library(plyr) #pour join
library(XML)
library(xml2) #Notamment pour read_htm
library(textclean) #Notamment pour replace_html
library(stringr) #Notamment pour str_replace
library(RCurl) #notamment pour getURL
library(rvest) #pour html_nodes
library(stringi) #pour stri_trans_tolower (tolower de base génère une erreur)
library(vtreat) #pour retraitement préalable pour XGBoost
library(magrittr) #pour use_series
library(xgboost) #pour XGBoost
library(dplyr) #pour %>%
library(pROC) #pour ROC et AUC
library(qdap) #Replace_abbreviation
library(qdapTools) #rm_nchar_words
library(tm) #removePunctuation
library(ggplot2) #pour graphiques ggplot
library(caret) #notamment pour varImp glm
library(installr) #pour is.empty
###########################################################################################
Fonctions de récupération des pages HTML
Les fonctions suivantes permettent de récupérer et de nettoyer les données issues des pages html précédemment créées, et d’enrichir notre jeu de données « AllDataKeywords » avec les nouvelles variables.
#
#
#######################################################
### Fonctions pour récupération des données HTML
getFilepath @^_|~.{}]", " ", text)
#On remplace les caractères spéciaux par des blancs (autre méthode)
text 0) {
AllDataKeywords$title.size[i] 0)
#### Meta description
description % html_nodes(xpath = '//meta[@name="description"]') %>%
html_attr('content') %>% stringi::stri_trans_tolower()
description_clean 0) {
#str(description)
AllDataKeywords$description.size[i] 0) {
if (length(keyword_clean) > 0) AllDataKeywords$description.clean.keyword.count[i] 0)
}
#### Meta Keywords ##### de la page ne pas confondre avec le keyword ciblé sur Yooda
keywords % html_nodes(xpath = '//meta[@name="keywords"]') %>%
html_attr('content') %>% stringi::stri_trans_tolower()
keywords_clean 0) {
AllDataKeywords$keywords.size[i] 0) {
if (length(keyword_clean) > 0) AllDataKeywords$keywords.clean.keyword.count[i] 0)
# cat("keywords =", keywords, "\n")
}
###### H1 #######
h1 0) {
allH1 0) {
if (length(keyword_clean) > 0) AllDataKeywords$allH1.clean.keyword.count[i] 0)
}
###### H2 #######
h2 0) {
allH2 0) {
if (length(keyword_clean) > 0) AllDataKeywords$allH2.clean.keyword.count[i] 0)
}
###### H3 #######
h3 0) {
allH3 0) {
if (length(keyword_clean) > 0) AllDataKeywords$allH3.clean.keyword.count[i] 0)
}
###### H4 #######
h4 0) {
allH4 0) {
if (length(keyword_clean) > 0) AllDataKeywords$allH4.clean.keyword.count[i] 0)
}
###### H5 #######
h5 0) {
allH5 0) {
if (length(keyword_clean) > 0) AllDataKeywords$allH5.clean.keyword.count[i] 0)
}
###### H6 #######
h6 0) {
allH6 0) {
if (length(keyword_clean) > 0) AllDataKeywords$allH6.clean.keyword.count[i] 0)
}
#### strong #####
strong 0) {
allStrong 0) {
if (length(keyword_clean) > 0) AllDataKeywords$allStrong.clean.keyword.count[i] 0)
}
#### b : bold #####
b 0) {
allB 0) {
if (length(keyword_clean) > 0) AllDataKeywords$allB.clean.keyword.count[i] 0)
}
#### p #####
p 0) {
allP 0) {
if (length(keyword_clean) > 0) AllDataKeywords$allP.clean.keyword.count[i] 0
}
#### body #####
body 0) {
allBody 0) {
if (length(keyword_clean) > 0) AllDataKeywords$allBody.clean.keyword.count[i]
Phase de récupération des données
Dans cette partie nous réalisons effectivement l’enrichissement des données. Les données sont récupérées à partir du fichier YoodaTechDataKeywords.csv qui doit se trouver dans le répertoire courant. Les fichiers de pages HTML doivent se trouver dans des sous répertoires (un par site). par exemple le répertoire « bazar-bio.fr-961671 » contient toutes les pages HTML que l’on a récupérées du site bazar-bio.fr.
Cette opération étant lourde en mémoire nous avons éclaté le jeu de données de 149355 enregistrements en plusieurs jeux de données de 5000 enregistrements que nous sauvegardons au fur et à mesure sur le disque dur.
#
#
#######################################################
#### Récupération des données des fichiers HTML des pages
memory.limit()
memory.limit(size=80000) #augmentation de la mémoire j'ai 12 GO donc 8 pour R et 4 pour tout le reste ....
gc()
#Chargement du fichiers des observations précédentes :
AllDataKeywords 5000 obs. (pb de mémoire)
chunk chunk) {
ListObsToRead
Sélection des données
Ici nous allons sélectionner les variables .
Si vous n’avez pas réussi à créer le fichier précédent vous pouvez le récupérer sur notre compte GitHub. et le dézipper dans le répertoire courant.
Cette fois-ci, pour changer, nous allons rechercher les facteurs explicatifs pour les pages positionnées en première place (istop1pos) dans les résultats de Google. Pour cela nous allons sélectionner 96 variables potentielles.
#
#
#############################################################################
### Machine Learning sur les données intéressantes
#############################################################################
#############################################################################
### Creation du fichier de données à tester, de train et de test
#### Sélection des variables
#############################################################################
AllDataKeywords 0 ),]
str(AllDataKeywords, list.len=255) #forte baisse plus que 46883 observations
#on sauvagarde les données électionnées 46883 lignes.
write.csv2(AllDataKeywords, file = "YoodaTechContentDataKeywordsSelect.csv", row.names = FALSE) #données 46883
########################################################################################################
AllDataKeywords
Modèle XGBoost
Ici nous faisons travailler l’algorithme XGBoost à proprement parlé.
#
#
#######################################################################################
# XGBoost sur istop1pos
########################################################################################
#Traitements préalables des données pour être utilisées par XGBoost
# Création d'un "plan de traitement" à partir de train (données d'entrainement)
# ici le système va créer des variables supplémentaires booléennes pour différents niveaux de facteurs dans les
# variables originales. one hot encoding
treatplan %
use_series(scoreFrame) %>%
filter(code %in% c("clean","lev")) %>% # get the rows you care about
use_series(varName)) # get the varName column
# Preparation des données d'entrainement à partir du plan de traitement créé précédemment
train.treat %
summarize(ntrees.train = which.min(train_error_mean), # find the index of min(train_rmse_mean)
ntrees.test = which.min(test_error_mean)) ) # find the index of min(test_rmse_mean)
#on prend le plus petit des 2
ntrees = min(Twotreesvalue$ntrees.train, Twotreesvalue$ntrees.test)
# The number of trees to use, as determined by xgb.cv
ntrees #53
# Run xgboost
xgbmod
Voici ce que nous donne la courbe ROC-AUC :
L’AUC (Area under the curve) est ici de 0,80808 par rapport à 0,78442, précédemment. Nous avons encore amélioré la validité du modèle et ce malgré une perte importante d’observations : 46883 au lieu de 149355.
Quelles sont les variables importantes :
Oups ! la variable qui explique le mieux le modèle est la taille du contenu de la balise Keywords : « keywords.size_clean » . Or cela fait plusieurs années que Google nous dit que cette balise n’est plus utilisée !!!!!
Sens de l’importance des variables
investiguons plus avant pour voir dans quels sens les variables influencent le modèle.
Ici testons plus particulièrement keywords.size_clean.
#
#
#Dans quel sens ????
train.treat.istop1pos
Boites de dispersions :
Sur le jeu d’entrainement :
Sur le jeu de test :
Comme on peut le voir sur les boites de dispersion (et aussi sur les données fournies par summary()) il semble que plus la balise keywords est renseignée plus la probabilité d’être sur une page positionnée en 1 est élevée…
Autre méthode
Cette méthode permet de déterminer les facteurs qui influencent positivement ou négativement une prédiction.
#
#
###################################################################
#Autre méthode de mesure d'importance
###################################################################
###################################################################
#explications : https://amunategui.github.io/actionable-instights/index.html
#Predictions (rappel)
pred.xgbmod (nrow(train.treat)/2)) {
cat("feature NA",feature,"\n")
test.treat_trsf[,feature] 0.5),]
#str(predsTRUE) #verif
#tail(predsTRUE) #verif
count.Pos_X1
Le graphique suivant indique les 10 facteurs qui apparaissent le plus souvent en position 1 pour expliquer positivement par rapport à la moyenne une prédiction vrai de positionnement sur la première place des résultats de Google (ouf!!!).
En d’autres termes et par exemple : le fait d’avoir une taille de balise « keywords » plus grande que la moyenne des observations semble indiquer que la probabilité que l’on ait une page en position 1 dans les SERPs de Google est plus élevée.
Comme on l’a vu précédemment cette tendance est contraire à l’avis des experts et aux indications de Google.
Conclusion
A ce stade il apparait que certaines informations fournies par le Machine Learning aillent en contradiction avec la littérature habituelle sur le SEO.
Soit tous les experts se sont trompés et Google nous a trompé depuis plusieurs années (ce qui est peu probable, quoique pour Google … 🙂 c’est moins sûr…).
Soit les variables que nous avons trouvées ne sont pas des variables explicatives mais des variables corrélées dans notre cas avec d’autres variables explicatives cachées.
Soit le modèle est sur-ajusté aux données que nous avons.
Soit encore un facteur qui nous échappe pour l’instant.
Il conviendra d’être prudent et de continuer à tester d’autres variables potentielles.
Merci pour votre avis en commentaires,
Pierre
