1ère édition du SEOGoodVibes le 29 mai 2020 : https://www.seogoodvibes.com
Bien plus que du Web Scraping, découvrez les portes que vous ouvrent Puppeteer, Node.js et Rendertron.
Audrey Schoonwater
4. Node.js en bref
But : Plateforme rendre le JavaScript côté serveur pour les robots
● Depuis 2009
● Développer des applications JavaScript (moteur JS v8 de Chrome)
● Permet d'exécuter du JavaScript très bas niveau et côté serveur.
5. Après l’install Node.js
Le dossier de “nodejs” contient :
● Exécuteur node.exe
● Module npm (Node Package Manager) :
ajouter / retirer des librairies
● Variables d'environnements pour exécuter les
commandes node et npm dans l’invite de
commande.
6. Hello World ! 1/2
Créer un dossier pour les projets node.js : C:nodejs
1. Créer helloworld.js
2. Coller le code suivant :
1. Exécuter le programme
1 // écrit dans la console "Hello World !"
2 console.log("Hello World !");
7. Hello World ! 2/2
Créer le fichier server.js avec le code suivant
1 const { createServer } = require('http'); // Librairie HTTP native
2 //Création du serveur
3 const server = createServer((request, response) => {
4 response.writeHead(200, {'Content-Type': 'text/plain'});
5 response.end('Hello Worldn');
6 });
7 server.listen(3000, () => console.log(`Adresse serveur : http://localhost:3000`));
8.
9. Prérequis
De quoi a-t-on besoin pour utiliser Puppeteer ?
● Lignes de commande
Connaissance basique
● JavaScript
Plateforme Node.js
● DOM Document Object Model
Connaissance basique
10. Qu’est-ce que Puppeteer ?
● Librairie développée par Google en 2017
● Elle contrôle une instance du navigateur Chrome ou
Chromium via le protocole DevTools.
● Cette instance peut être headless ou non, c’est-à-dire un
navigateur sans interface utilisateur.
● Il permet à un programme de lire et d’interagir avec lui.
11. Qu'est-il possible de faire avec Puppeteer ?
Tout de qu'on peut faire avec un navigateur :
● afficher une page
● cliquer sur un bouton
● remplir un formulaire etc.
12. Installer Puppeteer
• puppeteer-core librairie légère, fonctionne avec n’importe quel navigateur qui base sur le
protocole DevTools sans installer Chromium
• puppeteer librairie installant Chromium
> npm install puppeteer-core
> npm install puppeteer
14. 1. Visiter un site headless
• Visiter un site en mode headless
• Résultat : pas d’affichage car Puppeteer est headless = sans UI
const puppeteer = require('puppeteer');
puppeteer.launch({headless:true}).then(async nav => {
const page = await nav.newPage();
await page.setViewport({ width: 1280, height: 800 })
await page.goto('https://www.lemonde.fr');
await nav.close();
});
visitersite.js
15. 2. Visiter un site
• Visiter un site en mode headless activé
• Résultat : le navigateur Chrome s’ouvre, lance https://www.lemonde.fr dans un onglet puis
se referme
const puppeteer = require('puppeteer');
puppeteer.launch({headless:false}).then(async nav => {
const page = await nav.newPage();
await page.setViewport({ width: 1280, height: 800 })
await page.goto('https://www.lemonde.fr');
await nav.close();
});
visitersiteheadlessfalse.js
16. 3. Capturer toute la page
• l’option fullPage permet de capturer toute la page :
• Résultat : Capture de la page / bugue parfois
const puppeteer = require('puppeteer');
puppeteer.launch().then(async nav => {
const page = await nav.newPage();
await page.setViewport({ width: 1280, height: 800 })
await page.goto('http://news.google.fr/news');
await page.screenshot({
path: 'googlenews.png', fullPage: true
});
await nav.close();
})
capturer-toute-la-page.js
17. 4. Émuler et capturer sur iPhone
• Installer la librairie puppeteer/DeviceDescriptors pour émuler un
terminal à choisir dans la liste
• Résultat : Capture de la page / bugue parfois
const puppeteer = require('puppeteer');
const devices = require('puppeteer/DeviceDescriptors');
const iPhonex = devices['iPhone X']; puppeteer.launch().then(async nav => {
const page = await nav.newPage();
//On utilise page.emulate donc inutile de définir le viewport
await page.emulate(iPhonex);
await page.goto('https://www.nytimes.com/');
await page.screenshot({ path: 'nytimes-iphoneX.png' });
await nav.close();
});
emuleriphone.js
18. 5. Capturer le header
• Installer la librairie puppeteer/DeviceDescriptors pour émuler un
terminal à choisir dans la liste
• Résultat :
const puppeteer = require('puppeteer');
const options = { path:'nytimesheader.png',fullPage:false,clip:{ x:0,y:280,width:1280,height:150} };
puppeteer.launch().then(async nav => {
const page = await nav.newPage();
await page.setViewport({ width: 1280, height: 800
})
await page.goto('https://www.nytimes.com');
await page.screenshot(options);
await nav.close();
});
headerscreenshotpdf.js
19. 6. Bloquer les images
• Capturer Google News sans les images
• Résultat : ci-contre
const puppeteer = require('puppeteer'); (async () => {
const nav = await puppeteer.launch();
const page = await nav.newPage();
await page.setRequestInterception(true);
page.on('request', request => {
if (request.resourceType() === 'image')
request.abort();
else
request.continue();
});
await page.goto('https://news.google.fr/news/');
await page.screenshot({path: 'news.png', fullPage: true});
await nav.close();
})();
block-images.js
20. 7. Récupérer le <title> d’une page
• Résultat : ci-contre
const puppeteer = require('puppeteer');
puppeteer.launch().then(async nav => {
const page = await nav.newPage();
await page.goto('https://www.nytimes.com');
const title = await page.title()
console.log(title)
await nav.close();
});
title.js
21. 8. Saisie au clavier
• Saisir une requête dans la zone de recherche Google.fr
keyboard.png contient une impression de
l’émulation mobile du nytimes.com
• Résultat : ci-contre
clavier.js
const puppeteer = require('puppeteer');
(async () => { const browser = await puppeteer.launch();
const page = await browser.newPage()
await page.goto('https://trix-editor.org/’)
await page.focus('trix-editor’)
await page.keyboard.type("J'ajoute un titre ici avec Puppeteer via : ")
await page.screenshot({
path: 'keyboard.png’
})
await browser.close()
console.log("saisie au clavier");
})();
22. 8. Saisie au clavier
• Saisir une requête dans la zone de recherche Google.fr
keyboard.png contient une impression de l’émulation
mobile du nytimes.com
• Résultat : ci-contre
saisieclavier.js
const puppeteer = require('puppeteer');
const devices = require('puppeteer/DeviceDescriptors');
const iPhonex = devices['iPhone X']; (async () => {
const nav = await puppeteer.launch()
const page = await nav.newPage()
await page.emulate(iPhonex);
await page.goto('https://www.google.fr')
await page.focus('#tsf > div:nth-child(2) > div.A7Yvie.emca > div.zGVn2e > div > div.a4bIc > input')
await page.keyboard.type('newyork times');
await page.screenshot({ path: 'keyboard.png' })
await nav.close()
})()
23. 9. Récupérer les liens d’un site
const puppeteer = require("puppeteer");
// on utilise async/await donc on a besoin d’une fonction async à lancer
const lancer = async () => { // ouvrir le navigateur et préparer une page
const nav = await puppeteer.launch();
const page = await nav.newPage(); // ouvrir la page à scraper
await page.goto(‘https://news.ycombinator.com’);
// exécuter le JS dans le contexte de la page pour obtenir tous les liens
depuis la collections de nœuds Nodelist
const liens = await page.evaluate(() =>
// récupérer les liens dans un tableau depuis NodeList
Array.from(document.querySelectorAll("a")).map(anchor => [anchor.href,
anchor.textContent])
);
// afficher les liens
console.log(liens);
await nav.close();
};
// lancer la fonction asynchrone
lancer();
Liens-du-site.js
24. 10. Compter les liens d’un site
const puppeteer = require('puppeteer')
async function compter () {
const nav = await puppeteer.launch()
const page = await nav.newPage()
await page.goto('https://news.ycombinator.com', {waitUntil: 'networkidle2'})
await page.evaluate(_ => {
window.scrollBy(0, window.innerHeight)
})
console.log('Combien de liens de classe storylink ?', (await page.$$('a.storylink')).length)
await nav.close()
}
compter()
compter.js
25. 11. Scrapper des liens d’un site
const puppeteer = require('puppeteer'); (async () => {
const nav = await puppeteer.launch();
const page = await nav.newPage();
await page.setExtraHTTPHeaders({Referer: 'https://news.ycombinator.com'})
await page.goto('https://news.ycombinator.com');
await page.waitForSelector('td.title > a');
const news = await page.evaluate(() => {
const liens =Array.from(document.querySelectorAll('td.title > a'))
return liens.map(link => link.href).slice(0, 10)
})
console.log(news);
await nav.close();
})();
Les 10 premiers liens de news.ycombinator.com
Scrapper-liens.js
26. puppeteer/examples/crawlsite.js crawle une SPA, app monopage, et crée :
• une arborescence du résultat dans ./output/<site slug>/crawl.json.
• en option : captures d'écran de chaque page visitée --screenshots
12. Crawler un site en JS
...
const fs = require('fs');
const del = require('del');
const util = require('util');
const puppeteer = require('puppeteer');
const sharp = require('sharp');
const URL= process.env.URL|| 'https://news.polymer-project.org/';
const SCREENSHOTS = process.argv.includes('--screenshots');
...
Crawler_site.js
27. puppeteer/examples/crawlsite.js crawle une SPA,
app monopage, et crée :
• une arborescence du résultat dans
./output/<site slug>/crawl.json.
• en option : captures d'écran de chaque page
visitée --screenshots
12. Crawler un site en JS
...
const fs = require('fs');
const del = require('del');
const util = require('util');
const puppeteer = require('puppeteer');
const sharp = require('sharp');
const URL = process.env.URL || 'https://news.polymer-project.org/';
const SCREENSHOTS = process.argv.includes('--screenshots');
...
28. puppeteer/examples/codecoverage.js pour mesurer le code coverage CSS/JS.
Exemple : vérifier si la stratégie de lazy loading est payante
• Installer les packages manquants : npm i chalk cli-table
• Lancer : node code_coverage.js
13. Code coverage
const puppeteer = require('puppeteer');
const chalk = require('chalk');
constTable = require('cli-table');
const URL= process.env.URL|| 'https://www.chromestatus.com/features';
30. Puppeteer peut :
• Visiter un site (headless ou non)
• Prendre une impression d’écran (img / pdf)
• Émuler et capturer la page sur mobile
• Capturer le header
• Bloquer les images
• Récupérer le titre d’une page <title>
• Saisie au clavier
• Scraper / Compter des liens d’un site
• Crawler un site en JS (SPA)
31. Puppeteer Recorder
Extension Chrome open source qui enregistre les interactions de votre navigateur
et génère un script Puppeteer.
• Ajoute waitForNavigation, setViewPort et d'autres clauses utiles.
• Génère un script Puppeteer prêt à être copié / collé.
• Indique les événements en cours d'enregistrement.
• Propose des options de configuration pour modifier le code généré.
• Utilisez-le pour créer des vérifications de navigateur Checkly sans écrire de code.
• Plugin Chrome Puppeteer Recorder
33. ● plus de panier cassé…
● plus d’API cassées
Surveillez avec Checkly !
34. ● plus de panier cassé
● plus d’API cassées…
Surveillez avec Checkly !
35. • Puppeteer-firefox est un projet expérimental supporté à 89%.
• Il automatise Firefox avec la même API Node.js que pour Chrome.
• Talk: https://youtu.be/MbnATLCuKI4
• https://www.npmjs.com/package/puppeteer-firefox
• Tweet de lancement
• Voir les fonctions supportées
Et pour Firefox ?
36. • Dans la console :
Visiter un site headless
// Seul l’import change !
const puppeteer = require('puppeteer-firefox');
(async () => {
const nav = await puppeteer.launch();
console.info(nav);
await nav.close();
})();
37. • à installer sur l’hébergement
• https://github.com/nesk/puphpeteer
Un équivalent en PHP :
Puphpeteer
50. Escaped fragment (2009 - 2015)
• Hashbang #! dans les URL de l’app AJAX
example.com/#!slug-vue
• Moteur de rendu (navigateur headless)
pour générer des snapshots HTML
• URL spécifiques (paramètre
_escaped_fragment_) pour mapper les
snapshots HTML aux URL hashbang :
example.com/?_escaped_fragment_=s
lug-vue
Méthode pour “crawler les sites en AJAX” proposée par Google
Navigateur Crawler
App JS
Requête
HTTP URL
hashbang
Serveur Web
Requête
HTTP URL
escaped
fragment
Snapshot
HTML
Extrait et
suit les
liens
“Headless browser”
⇨ Google crawle les URL _escaped_fragment_ et indexe leur contenu à la place des URL hashbang.
51. Dynamic Rendering
• Utilisation d’URL standard dans l’application
full JS : example.com/slug-page
• Moteur de rendu (navigateur headless) pour
générer des snapshots HTML
• Détection du User Agent pour servir
dynamiquement l’appli JS “client-side” aux
navigateurs classiques ou un snapshot HTML
aux moteurs de recherche
• URL uniques
• Performance optimale :
– Pages HTML relativement légères pour les bots
– Web app rapide pour les utilisateurs
• Mise en place du moteur de rendu
• Gestion du cache / rafraîchissement des
snapshots
Snapshots HTML pour les bots / Web app JS pour les utilisateurs
Navigateur Crawler
App JS
Serveur Web
Snapshot
HTML
Extrait et
suit les
liens
“Headless browser”
Requête HTTP
URL standard
⇨ Les bots voient des pages HTML complètes prêtes à l’indexation.
⇨ Les utilisateurs voient l’appli JS classique (HTML + JS côté client).
52. Server-Side Rendering
• Utilisation d’URL standard dans
l’application full JS : example.com/slug-
page
• Exécution du JS et génération du HTML
sur le serveur Node (Next.js pour React,
Nuxt.js pour Vue.js, Universal pour
Angular)
• Envoi d’une page HTML complète à tous
les visiteurs
• Du JS côté client peut enrichir ces pages
pour ajouter des éléments de
personnalisation et fluidifier l’UX pour
les Utilisateurs
HTML pour tous / surcouche JS optionnelle pour les utilisateurs
Navigateur / Crawler
Serveur Web
HTML
Extrait et
suit les
liens
Module de SSR
App
JS
Requête HTTP
URL “standard”
53. Server-Side Rendering
Stratégie à adapter selon le site
• Génération de pages
HTML statiques au
moment du build
• adapté aux sites
statiques avec peu
d’interactivité
• Personnalisation, UX
• Les pages HTML sont
générées à la
demande
• puis sont reprises en
charge par JavaScript
dans le navigateur
• Complexe mais
stratégie qui existe
Static SSR SSR avec
réhydratation
SSR en streaming avec
réhydratation
progressive
01 02 03
55. Comparer Temps de chargement - CSR vs SSR
A QUOI CA SERT ?
Visualiser le chargement d’une ou plusieurs URL
dans le navigateur.
Comparaison côte à côte
Les pages se lancent côte à côte
Centrer sur l’écran
Des options permettent de centrer la
fenêtre sur l’écran
Commande
Mobile / Bande passante
Emule le mobile et la bascule de
CPU/Limitation de bande passante
01
02
03
> node side-by-side-pageload.js -h
Client Side Rendering Server Side Rendering
56. CSR
devwebfeed.appspot.com
SSR
devwebfeed.appspot.com/ssr
Exécution de JavaScript côté client (navigateur) via un framework
JavaScript.
Lorsque la page est demandée, le HTML est livré au navigateur
et rendu en amont, JS et CSS déjà exécutés.
Temps de chargement
69%
Temps de chargement
75%
Comparer Temps de chargement - CSR vs SSR
57. Nombre de résultats Google Images
Autre idée d’utilisation avec Puppeteer :
• Avec le user agent Googlebot, le nombre de résultats est affiché dans Google Images.
Et pour un site donné via un site:example.com.
• Comparer le poids des images récupérées pour les comparer aux concurrents...
59. • Rendertron est une solution de rendering avec un Chrome Headless, pour sérialiser
des pages web à la volée.
• Démo : https://render-tron.appspot.com
● 🔨 Construit avec Puppeteer
● ☁️ Facile à déployer vers Google Cloud
● 🔍 Permet de vérifier si le rendu est ok pour le SEO
Qu’est-ce que Rendertron ?
60. • Comment ? middleware.
Pour quoi ? contenu correct à fournir
Quand ? bot n’interprète pas le JavaScript.
Rendertron : serveur HTTP autonome.
● requête par proxy du middleware.
● affiche les pages via Headless Chrome :
○ détecte le chargement de la PWA
○ sérialise la réponse à la demande d'origine.
● compatible technologies côté client et composants Web.
Comment ça marche ?
64. App avec Express & Rendertron-middleware
Configurer la liste de robots
Rendertron utilise l'en-tête HTTP "user-agent" pour déterminer si la requête provient d'un
robot ou du navigateur d'un utilisateur.
Il compare ces requêtes à une liste complète d'user-agents de robots.
Par défaut, cette liste n'inclut pas Googlebot, car il peut exécuter JavaScript. Pour que
Rendertron affiche également les requêtes Googlebot, il faut ajouter Googlebot à liste des
user-agents :
Rendertron comparera l'en-tête "user-agent" à cette expression régulière.
const BOTS = rendertron.botUserAgents.concat('googlebot');
const BOT_UA_PATTERN = new RegExp(BOTS.join('|’),'i’);
65. Tester avec Rendertron & GCP
• Le tutoriel Rendertron par Martin Splitt Mr JavaScript chez Google :
– https://webmaster-fr.googleblog.com/2019/01/affichage-dynamique-rendertron.html
• Quels sont les sites qui devraient utiliser
l'affichage dynamique ?
• Comment l'affichage dynamique fonctionne-t-il ?
• Exemple d'application Web
• Configurer le serveur
• Déployer une instance Rendertron
• Ajouter Rendertron au serveur
• Configurer la liste de robots
• Ajouter le middleware
• Tester la configuration
• Conclusion