La vitesse de chargement des pages est un élément qui prend de plus en plus d’importance dans le cœur de Google et de ses algorithmes. L’arrivée par étape de l’index Mobile First devrait aller en ce sens également puisque Google a rappelé à plusieurs reprises que le score PageSpeed deviendrait un critère pour les mobiles, en complément de l’ergonomie mobile. Il est donc temps de voir ce que nous réserve l’API PageSpeed Insights. Nouvelle année oblige, notre bonne résolution a été de présenter l’API avec PHP mais aussi avec Javascript. Amusez-vous bien ! 🙂

Par Mathieu Chartier

Notre aventure avec les API a déjà connu deux précédents dans les lettres Abondance de septembre et novembre 2017, nous vous conseillons donc vivement de vous référer à ces articles si vous voulez connaître les prérequis techniques pour utiliser les API de Google avec PHP. Dans le cas de Javascript, nous l’introduirons simplement dans cet article. Entrons d'ores et déjà dans le vif du sujet…

Google et la vitesse de chargement des pages, kezako ?

Avant toutes choses, rappelons que Google utilise la vitesse de chargement de deux manières, comme cela a été confirmé à plusieurs reprises. D’une part, Google effectue un calcul qui lui permet d’évaluer le temps d’accès au serveur d’un site et de mesurer l’impact que cela aura sur son crawl budget. Dans ce cas, GoogleBot cherche à savoir si cela vaut le coup de crawler une ou plusieurs pages d’un site. Ce facteur n’a aucune incidence sur le classement des pages, mais potentiellement sur l’indexation, car Google pourrait limiter son temps de crawl sur des sites plutôt lents par exemple. D’autre part, Google analyse le « PageSpeed score » (mentions courantes trouvées dans des brevets de Google notamment) comme un facteur de classement. Rien ne permet cependant de mesurer l’importance de ce critère dans l'algorithme global.

En d’autres termes, Google utilise le Time to first byte (TTFB) pour mesurer l’impact d’un crawl sur le budget d’exploration. Il s’agit d’une sorte de ping de serveur à serveur partant d’un datacenter de Google vers un site donné.

Dans un second temps, Google analyse une batterie de critères (PageSpeed) pour attribuer une note à la page en termes de vitesse. Contrairement à ce qui se dit régulièrement, Google ne calcule pas la vitesse brute d’une page, celle-ci serait bien trop fluctuante selon l’heure, le jour, la qualité du serveur et du réseau, etc. Par conséquent, Google a créé une liste des règles qui lui permet de penser que les pages testées ont tout mis en œuvre pour être rapides. Cela signifie qu’une page pourrait très bien avoir une bonne note au PageSpeed, tout en étant plutôt « lente » en réalité. Pourtant, nous remarquons que l’adéquation entre les deux phénomènes est souvent bonne, donc d’un bon PageSpeed score résulte souvent une page efficace et rapide.


L’arrivée de l’index Mobile First devrait donner encore plus de valeur au PageSpeed Score, c’est pourquoi il peut s’avérer intéressant de corriger au mieux la note finale sur 100 attribuée par Google. Nous vous conseillons de lire cette ressource très instructive de Google au sujet de son analyse mobile (source : https://goo.gl/m6LXWd). Certes, beaucoup de facteurs impliquent une bonne maîtrise technique mais dites-vous que le jeu en vaut la chandelle pour vos utilisateurs, et par voie de conséquence pour la note du PageSpeed.

Quelles données récupérer avec l’API PageSpeed Insights ?

L’API PageSpeed Insights permet d’extraire toutes les données utiles provenant de l’outil originel de Google, à savoir le PageSpeed score, les règles à corriger, à optimiser ou déjà bien gérées. En outre, le tableau de résultat d’un test via l’API offre des données sur les ressources web chargées et leur poids dans la page. Nous pouvons donc obtenir des tableaux complets qui retracent l’ensemble des facteurs à améliorer, que ce soit pour ordinateur ou pour mobile (selon les paramètres choisis lors de la connexion à l’API).

Notons toutefois que Google met en cache les pages pendant 30 secondes lors d’un test, alors n’actualisez pas trop souvent le programme si vous voulez tester à nouveau une page.

Nous pouvons déplorer le fait que la « v2 » de l’API PageSpeed Insights ne permet pas de récupérer des archives contenant les ressources compressées comme le propose l’outil PageSpeed Insights habituellement. En effet, l’outil ajoute un lien contenant des ressources compressées (JS, CSS, images…) ainsi que quelques optimisations dans un .zip en-dessous de ces préconisations. Il aurait été vraiment intéressant que l’API permette de récupérer ces données afin d’effectuer des traitements par lot pour pléthores d’URL d’un site. Espérons que la troisième version de l’API proposera cela, et pas seulement la méthode « runpagespeed ».


Fig. 1. Téléchargement de ressources optimisées avec l’outil PageSpeed Insights.

Comment utiliser l’API PageSpeed Insights avec PHP ?

L’usage de l’API est extrêmement simplifiée car aucune connexion OAuth ou par clé de service n’est nécessaire, il convient juste d’obtenir une clé d’API dans la console de Google (source : https://console.cloud.google.com/apis/) pour pouvoir profiter du programme en ligne. Dans un second temps, n’oubliez pas de télécharger et d’installer la librairie open source Google PHP API Client que nous avons présentée dans les précédentes lettres d’Abondance.

La connexion à l’API va donc être rapide, notre fichier PHP doit commencer ainsi, une fois l’API activée dans la console de Google :

<?php
// Autoload des classes PHP pour utiliser les API de Google
require_once('google-api-php-client-2.2.0/vendor/autoload.php');

// Création d'une connexion aux API
$client = new Google_Client();

// Ajouter un Guzzle (pour cURL en PHP)
$http = new GuzzleHttpClient([
            'verify' => false
]);
$client->setHttpClient($http);
$client->authorize($http);

// Définition d'un nom d'application (optionnel)
$client->setApplicationName("API_PageSpeed_Insights_Exemple");

// Authentification par clé d'API (suffisant pour l'API PageSpeed Insights)
$client->setDeveloperKey("CLE_API_PAGESPEED_INSIGHTS"); // À modifier !

// Appel de la méthode de connexion à l'API PageSpeed Insights
$api_pso = new Google_Service_Pagespeedonline($client);

La suite n’est pas bien plus compliquée, il ne nous reste qu’à ajouter les paramètres optionnels et à lancer la méthode « runpagespeed » de l’API. Nous pouvons obtenir de nombreuses informations sur les divers paramètres et résultats obtenus dans la documentation de Google (source : https://goo.gl/hmHVGf). Notons que seule l’URL à tester est obligatoire, même si nous conseillons de placer le critère « locale » sur « fr_FR ».

$url = "URL_À_TESTER";

// Liste des paramètres optionnels
$params = array(
            "strategy" => "mobile", // "desktop" ou "mobile"
            "locale" => "fr_FR", // "en_US", "fr_FR"...
            // "rule" => "", // nom d'une règle (commenter si vous voulez tout vérifier)
            "screenshot" => false, // true/false
            "filter_third_party_resources" => false, // true/false
);
$runPageSpeedApi = $api_pso->pagespeedapi->runpagespeed($url, $params); // Lance l'API

// Obtention du tableau complet des résultats (peu lisible au premier abord)
var_dump($runPageSpeedApi);

Obtention des résultats dans des tableaux lisibles

Nous avons réussi à obtenir un tableau de données qui sont pour l'instant quasi illisibles, c’est pourquoi il est préférable de n’enregistrer que les données nécessaires. Nous avons donc réalisé un exemple de ce qui peut se faire simplement avec PHP en enregistrant les données majeures dans un tableau, comme le montre la capture suivante pour un test de la page d’accueil d’abondance.com.


Fig. 2. Résultat formaté via PHP pour un test avec l’API PageSpeed Insights.

Le code permettant d’effectuer ceci est proposé ci-dessous. Il est totalement personnalisable selon les envies de chacun, il ne s’agit que d’un exemple. L’objectif est surtout de nous permettre de cibler les données principales listées dans le tableau illisible fourni à l’origine par l’API.

Nous avons ajouté la valeur « impact de la règle » qui est fournie par l’API. Cela correspond à une mise en concurrence des diverses règles pour savoir à quel point une règle pourrait impacter positivement la note du PageSpeed. Plus le chiffre est élevée, plus la règle est à corriger et importante pour Google.

// Récupération des données intéressantes
// Score
$pageSpeedScore = $runPageSpeedApi['ruleGroups']['SPEED']['score'];
// Ressources chargées
$nbRessourcesTotal = $runPageSpeedApi['pageStats']['numberResources'];
$nbJs = $runPageSpeedApi['pageStats']['numberJsResources'];
$nbCss = $runPageSpeedApi['pageStats']['numberCssResources'];
$nbStatic = $runPageSpeedApi['pageStats']['numberStaticResources'];
$nbDiffHosts = $runPageSpeedApi['pageStats']['numberHosts'];
// Poids des données
$htmlWeight = number_format(($runPageSpeedApi['pageStats']['htmlResponseBytes'] / 1024), 2, ",", " ")." Ko";
$cssWeight = number_format(($runPageSpeedApi['pageStats']['cssResponseBytes'] / 1024), 2, ",", " ")." Ko";
$jsWeight = number_format(($runPageSpeedApi['pageStats']['javascriptResponseBytes'] / 1024), 2, ",", " ")." Ko";
$imagesWeight = number_format(($runPageSpeedApi['pageStats']['imageResponseBytes'] / 1024), 2, ",", " ")." Ko";
$textWeight = number_format(($runPageSpeedApi['pageStats']['textResponseBytes'] / 1024), 2, ",", " ")." Ko";
$requestWeight = number_format(($runPageSpeedApi['pageStats']['totalRequestBytes'] / 1024), 2, ",", " ")." Ko";
$otherWeight = number_format(($runPageSpeedApi['pageStats']['otherResponseBytes'] / 1024), 2, ",", " ")." Ko";
$totalWeight = $htmlWeight + $cssWeight + $jsWeight + $imagesWeight + $textWeight + $requestWeight + $otherWeight." Ko";
// Règles du score PageSpeed
$rules = $runPageSpeedApi['formattedResults']['ruleResults']; // Tableau des règles

// Ordonne les règles par impact sur le score PageSpeed (plus grand au plus petit)
function impactCompare($a, $b){
    return $a['ruleImpact'] > $b['ruleImpact'];
}
usort($rules, 'impactCompare');

// Affichage dans un tableau propre
echo "<table style='text-align:center; background:#eee; border-collapse:collapse; font-family:sans-serif;'>";
echo "<tr><th colspan='3' style='background:#242424; color:#eee; padding:.5em'>Général</th></tr>";
echo "<tr><th>URL</th><td>".$url."</td></tr>";
echo "<tr><th>PageSpeed Score</th><td>".$pageSpeedScore."</td></tr>";
echo "<tr><th colspan='3' style='background:#242424; color:#eee; padding:.5em'>Ressources de la page</th></tr>";
echo "<tr><th>Ressources CSS</th><td>".$nbCss."</td></tr>";
echo "<tr><th>Ressources JS</th><td>".$nbJs."</td></tr>";
echo "<tr><th>Ressources statiques (HTML...)</th><td>".$nbStatic."</td></tr>";
echo "<tr><th>Nombre d'hôtes différents appelés</th><td>".$nbDiffHosts."</td></tr>";
echo "<tr><th>Ressources total à charger</th><td>".$nbRessourcesTotal."</td></tr>";
echo "<tr><th colspan='3' style='background:#242424; color:#eee; padding:.5em'>Poids par type de donnée</th></tr>";
echo "<tr><th>Poids de l'HTML</th><td>".$htmlWeight."</td></tr>";
echo "<tr><th>Poids du CSS</th><td>".$cssWeight."</td></tr>";
echo "<tr><th>Poids du Javascript</th><td>".$jsWeight."</td></tr>";
echo "<tr><th>Poids des images</th><td>".$imagesWeight."</td></tr>";
echo "<tr><th>Poids des textes</th><td>".$textWeight."</td></tr>";
echo "<tr><th>Poids des requêtes</th><td>".$requestWeight."</td></tr>";
echo "<tr><th>Poids des autres données</th><td>".$otherWeight."</td></tr>";
echo "<tr><th>Poids total de la page</th><td>".$totalWeight."</td></tr>";
echo "<tr><th colspan='3' style='background:#242424; color:#eee; padding:.5em'>Règles du score PageSpeed</th></tr>";
echo "<tr><th>Nom de la règle</th><th>Impact de la règle</th></tr>";
foreach($rules as $rule) {
            echo "<tr><th>".$rule['localizedRuleName']."</th><td>".$rule['ruleImpact']."</td></tr>";
            if($rule['ruleImpact'] != 0 && isset($rule['urlBlocks'])) {
                       foreach($rule['urlBlocks'] as $urlBlock) {
                                   if(isset($urlBlock['header']['args'])) {
                                               echo "<tr>";
                                               foreach($urlBlock['header']['args'] as $arg) {
                                                          echo "<td><em>".$arg['value']."<em></td>";
                                               }
                                               echo "</tr>";
                                   }
                                   if(isset($urlBlock['urls'])) {
                                               foreach($urlBlock['urls'] as $urlBlockUrls) {
                                                          echo "<tr>";
                                                          foreach($urlBlockUrls['result']['args'] as $arg) {
                                                                      echo "<td><em>".$arg['value']."<em></td>";
                                                          }
                                                          echo "</tr>";
                                               }
                                   }
                       }
            }
}
echo "<table>";

Avec un tel code, l’affichage varie selon la qualité de la note et liste les ressources à optimiser ou à corriger. Par exemple, quand la note du PageSpeed est plutôt bonne, le tableau affiche peu de données, comme dans la capture suivante.


Fig. 3. Exemple d’un test pour un bon PageSpeed Score.

Gestion de l’API PageSpeed Insights avec Javascript

L’API PageSpeed Insights étant plutôt simple à utiliser (pas de connexion OAuth nécessaire notamment), elle est parfaite pour introduire l’usage du Javascript pour obtenir des données sur les pages web. En effet, depuis nos premières lectures au sujet des API, nous avons uniquement traité de PHP avec la librairie Google PHP API Client, il est donc temps d’introduire Javascript dans notre belle aventure.

Il existe plusieurs manières pour effectuer un appel à l’API en Javascript, nous en dévoilerons seulement deux ici. La première technique est directement inspirée de la documentation de Google (source : https://goo.gl/QpZA7z) tandis que la seconde constitue un appel AJAX. Ces deux méthodes (séparée par des commentaires) appellent chacune une fonction de retour (callback) et sont à placer dans une balise <script> dans le code HTML. La fonction setTimeout() est utilisée pour appeler les fonctions (en théorie, nous ne devons appeler qu’une seule des deux méthodes).

// Données utiles (variables)
const apiTest = 'URL_À_TESTER'; // URL à tester
const apiKey = 'CLE_API_PAGESPEED_INSIGHTS'; // Clé d'API
const apiUrl = 'https://www.googleapis.com/pagespeedonline/v2/runPagespeed?'; // URL d'API

/*====================*/
/*=== APPEL DIRECT ===*/
/*====================*/
// Active le PageSpeed côté DOM (création de la requête)
function createApiCall() {
            let s = document.createElement('script'); // Créé une balise <script>
            s.type = 'application/javascript'; // Ajout du type
            s.async = true; // Ajout de async="true" (asynchrone)
            let query = [
            'url='+apiTest, // Url à tester
            'callback=pagespeedCallback', // Callback
            'key='+apiKey, // Clé d'API
            "strategy=desktop", // "desktop" ou "mobile"
                       "locale=fr_FR", // "en_US", "fr_FR"...
                       // "rule" => "", // nom d'une règle (commenter si vous voulez tout tester)
                       "screenshot=false", // true/false
                       "filter_third_party_resources=false", // true/false
            ].join('&');
            s.src = apiUrl + query; // Ajout de l'URL d'appel à l'API
            document.head.insertBefore(s, null); // Insertion de la balise dans le DOM
}
setTimeout(createApiCall, 0); // Appel asynchrone avec setTimeout (conseillé)

/*=====================*/
/*=== APPEL EN AJAX ===*/
/*=====================*/
var createApiCallAjax = function() {
            // Conception de l'URL finale pour l'appel de l'API
            const queryString = [
                'url=' + apiTest, // Url à tester
                'key=' + apiKey, // Clé d'API
            "strategy=desktop", // "desktop" ou "mobile"
                       "locale=fr_FR", // "en_US", "fr_FR"...
                       // "rule" => "", // nom d'une règle (commenter si vous voulez tout tester)
                       "screenshot=false", // true/false
                       "filter_third_party_resources=false", // true/false
            ].join('&');
            const query = apiUrl + queryString;

            // Ouvrir l'objet XMLHttpRequest pour Ajax (en réponse JSON)
            var xhr = new XMLHttpRequest();
            xhr.responseType = 'json';

            // Controle les changements d'etat de l'Ajax
            xhr.onreadystatechange = function() {
                       // Verifie que l'etat en cours est fonctionnel
                       if(xhr.readyState === XMLHttpRequest.DONE) {
                                   pagespeedCallback(xhr.response); // Appel du Callback
                       }
            }

            // Ouvre l'URL de destination et lance la requete Ajax
            xhr.open('GET', query);
            xhr.send();
}
setTimeout(createApiCallAjax, 0); // Appel asynchrone avec setTimeout (conseillé)

Les deux fonctions appellent une fonction de retour appelée pagespeedCallback(). C’est cette dernière qui nous permet d’afficher les résultats dans la console mais aussi de formater les résultats. Pour ne pas trop nous perdre, nous avons volontairement repris la même logique d’affichage que pour la version PHP étudiée précédemment. Par conséquent, les résultats vont offrir le même type de tableau final, affiché dans le <body> d’une page HTML. Il suffit uniquement de copier le code suivant à la suite d’une des méthodes d’appel de l’API présentée ci-dessus.

/*==========================*/
/*=== Callback (réponse) ===*/
/*==========================*/
function pagespeedCallback(response) {
            // Gestion des erreurs (si elles existent)
            if(response.error) {
                       // Récupération des erreurs dans "response" (JSON)
                       var errors = response.error.errors;
                       for (var i = 0, len = errors.length; i < len; ++i) {
                                   console.log(errors[i].message); // Inscrit l'erreur dans la console
                       }
                       return;
            }

            // Log dans la console
            console.log(response);

            // Récupération des données intéressantes
            // Score
            let pageSpeedScore = response['ruleGroups']['SPEED']['score'];
            // Ressources chargées
            let nbRessourcesTotal = response['pageStats']['numberResources'];
            let nbJs = response['pageStats']['numberJsResources'];
            let nbCss = response['pageStats']['numberCssResources'];
            let nbStatic = response['pageStats']['numberStaticResources'];
            let nbDiffHosts = response['pageStats']['numberHosts'];
            // Poids des données
            let htmlWeight = (!isNaN(response['pageStats']['htmlResponseBytes'])) ? parseInt(response['pageStats']['htmlResponseBytes'] / 1024) : 0;
            let cssWeight = (!isNaN(response['pageStats']['cssResponseBytes'])) ? parseInt(response['pageStats']['cssResponseBytes'] / 1024) : 0;
            let jsWeight = (!isNaN(response['pageStats']['javascriptResponseBytes'])) ? parseInt(response['pageStats']['javascriptResponseBytes'] / 1024) : 0;
            let imagesWeight = (!isNaN(response['pageStats']['imageResponseBytes'])) ? parseInt(response['pageStats']['imageResponseBytes'] / 1024) : 0;
            let textWeight = (!isNaN(response['pageStats']['textResponseBytes'])) ? parseInt(response['pageStats']['textResponseBytes'] / 1024) : 0;
            let requestWeight = (!isNaN(response['pageStats']['totalRequestBytes'])) ? parseInt(response['pageStats']['totalRequestBytes'] / 1024) : 0;
            let otherWeight = (!isNaN(response['pageStats']['otherResponseBytes'])) ? parseInt(response['pageStats']['otherResponseBytes'] / 1024) : 0;
            let totalWeight = htmlWeight + cssWeight + jsWeight + imagesWeight + textWeight + requestWeight + otherWeight;
            // Règles du score PageSpeed
            let rules = response['formattedResults']['ruleResults']; // Tableau des règles

            // Affichage des résultats
            let result = "<tr><th colspan='3' style='background:#007db7; color:#eee; padding:.5em'>Général</th></tr>";
            result += "<tr><th>URL</th><td>"+apiTest+"</td></tr>";
            result += "<tr><th>PageSpeed Score</th><td>"+pageSpeedScore+"</td></tr>";
            result += "<tr><th colspan='3' style='background:#007db7; color:#eee; padding:.5em'>Ressources de la page</th></tr>";
            result += "<tr><th>Ressources CSS</th><td>"+nbCss+"</td></tr>";
            result += "<tr><th>Ressources JS</th><td>"+nbJs+"</td></tr>";
            result += "<tr><th>Ressources statiques (HTML...)</th><td>"+nbStatic+"</td></tr>";
            result += "<tr><th>Nombre d'hôtes différents appelés</th><td>"+nbDiffHosts+"</td></tr>";
            result += "<tr><th>Ressources total à charger</th><td>"+nbRessourcesTotal+"</td></tr>";
            result += "<tr><th colspan='3' style='background:#007db7; color:#eee; padding:.5em'>Poids par type de donnée</th></tr>";
            result += "<tr><th>Poids de l'HTML</th><td>"+htmlWeight+" Ko</td></tr>";
            result += "<tr><th>Poids du CSS</th><td>"+cssWeight+" Ko</td></tr>";
            result += "<tr><th>Poids du Javascript</th><td>"+jsWeight+" Ko</td></tr>";
            result += "<tr><th>Poids des images</th><td>"+imagesWeight+" Ko</td></tr>";
            result += "<tr><th>Poids des textes</th><td>"+textWeight+" Ko</td></tr>";
            result += "<tr><th>Poids des requêtes</th><td>"+requestWeight+" Ko</td></tr>";
            result += "<tr><th>Poids des autres données</th><td>"+otherWeight+" Ko</td></tr>";
            result += "<tr><th>Poids total de la page</th><td>"+totalWeight+" Ko</td></tr>";
            result += "<tr><th colspan='3' style='background:#007db7; color:#eee; padding:.5em'>Règles du score PageSpeed</th></tr>";
            result += "<tr><th>Nom de la règle</th><th>Impact de la règle</th></tr>";
            for(rule in rules) {
                       result += "<tr><th>"+rules[rule]['localizedRuleName']+"</th><td>"+rules[rule]['ruleImpact']+"</td></tr>";
                       if(rules[rule]['ruleImpact'] != 0 && rules[rule]['urlBlocks'] !== "undefined") {
                                   for(urlBlock in rules[rule]['urlBlocks']) {
                                               if(rules[rule]['urlBlocks'][urlBlock]['header']['args']) {
                                                          result += "<tr>";
                                                          for(arg in rules[rule]['urlBlocks'][urlBlock]['header']['args']) {
                                                                      result += "<td><em>"+rules[rule]['urlBlocks'][urlBlock]['header']['args'][arg]['value']+"<em></td>";
                                                          }
                                                          result += "</tr>";
                                               }
                                               if(rules[rule]['urlBlocks'][urlBlock]['urls']) {
                                                          for(urlBlockUrls in rules[rule]['urlBlocks'][urlBlock]['urls']) {
                                                                      result += "<tr>";
                                                                      for(arg in rules[rule]['urlBlocks'][urlBlock]['urls'][urlBlockUrls]['result']['args']) {
                                                                                  result += "<td><em>"+rules[rule]['urlBlocks'][urlBlock]['urls'][urlBlockUrls]['result']['args'][arg]['value']+"<em></td>";
                                                                      }
                                                                      result += "</tr>";
                                                          }
                                               }
                                   }
                       }
            }

            // Insertion dans le DOM
            let table = document.createElement('table'); // Créé une balise <script>
            table.id = 'pagespeed-result'; // ID du tableau
            table.style = 'text-align:center; background:#eee; border-collapse:collapse; font-family:sans-serif;'; // Ajout du CSS
            document.body.insertBefore(table, null); // Insertion de la balise dans le DOM
            document.getElementById('pagespeed-result').innerHTML = result;
}

Voilà, le tour est joué. Nous avons obtenu la même analyse qu’en PHP mais avec Javascript. Nous avons la possibilité de modifier cette fonction de callback comme bon nous semble, et de fait d’afficher les résultats de multiples manières. À vous de jouer… 😉

Conclusion

Nous maîtrisons désormais l’API PageSpeed Insights. Certes, cette dernière n’offre pas encore toutes les fonctionnalités que nous aurions pu souhaiter, mais elle a l’avantage de retourner toutes les informations nécessaires au suivi des critères du PageSpeed Score. Notre aventure autour des API n’est pas encore terminée, alors soyons patients, la suite est au prochain épisode…


Mathieu Chartier
Consultant-Formateur et webmaster indépendant, Internet-Formation (https://www.internet-formation.fr)