L’art du scraping III : Projet Product Hunt

Kévin Lemaire
Le Reacteur
Published in
6 min readJul 31, 2017

Le 3ème article de cette série sera l’occasion de revoir l’intégralité des notions abordées dans les 2 précédents tutoriels et d’appréhender la gestion d’une navigation de type ”infinite scroll”.

Pour ceux qui aurait manquer les 2 autres articles, voici les liens :

Conseil : Mettez toujours vos plus belles lunettes pour scraper des données.

Vous apprendrez comment :

  • Appréhender la pagination de type “infinite scroll”
  • Scroller une page afin de charger des éléments en ajax
  • Récupérer des éléments texte d’une page

Il s’agit toujours d’un exemple donné à titre éducatif uniquement (😈) !

Les objectifs

Pour ce projet, nous allons choisir une rubrique du site Product Hunt. Ce site met en avant les nouveaux produits hi-tech, tel que des apps, des livres, des podcasts et des objets connectés. C’est d’ailleurs cette dernière catégorie que nous allons utiliser pour l’exemple.

Lien utilisé pour l’exemple : https://www.producthunt.com/topics/wearables?subtopic=232

1- Charger une page de type “infinite scroll”

2- Vérifier que la page et tous les éléments ont été chargés

3- Récupérer les titres des produits

Du code, du code et du code

Pour bien commencer, créons un nouveau fichier JavaScript appelé : producthunt.js qui contiendra notre script.

~ touch producthunt.js

Être un des leurs

Tout comme dans le projet “LDLC”, nous allons personnaliser notre instance casper grâce à la fonction create(). Cette dernière accepte un argument unique, un objet JavaScript standard, contenant tous les paramètres. Pour rappel, cet objet va notamment contenir notre userAgent qui a pour but de faire croire que les requêtes exécutées par notre script sont celles d’un humain.

var casper = require("casper").create({
verbose: true,
logLevel: 'error',
pageSettings: {
loadImage: false,
loadPlugins: false,
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36'
}
});

Le coeur de notre script

Avant de rentrer dans la coeur du sujet, nous allons avoir besoin de 4 variables :

  • L’URL du site que nous allons scraper (url)
  • Un tableau pour stocker les titres des produits (titles)
  • Une variable scrolled pour suivre la hauteur de la page modifiée par les différentes actions de “scroll”
  • Une variable scrollDelta enregistrant la différence de hauteur de la page entre deux “scrolls”, et ainsi déterminer si le bas de la page a été atteint
var url = 'https://www.producthunt.com/topics/wearables?subtopic=232';
var titles = [];
var scrolled = 0;
var scrollDelta = null;

Pour gérer la navigation de type “infinite scroll”, nous allons créer 2 nouvelles variables qui seront complémentaires. La première scrollPage va nous permettre de scroller la page . La seconde isBottomPage va nous permettre de vérifier si la fin de la page est atteint pour éviter de scroller la page indéfiniment - Élémentaire, mon cher Watson.

La variable scrollPage va nous permettre d’atteindre la bas de la page et éventuellement charger de nouveaux produits. Pour cela, nous allons utiliser la fonction scrollToBottom(). Ensuite, nous allons mettre à jour les données contenues dans les variables scrollDelta et scrolled. Un console.log() nous permettra de suivre la progression du “scroll”. Enfin, nous allons appeler notre seconde variable isBottomPage qui nous permettra de vérifier si le bas de la page est atteint.

var scrollPage = function() {
casper.wait(1000, function() {
casper.scrollToBottom();
var newScrolled = casper.evaluate(function() {
return window.scrollY; // Mesurer la hauteur de la page
});
scrollDelta = newScrolled - scrolled;
scrolled = newScrolled;
console.log("Hauteur de la page : ", scrolled); isBottomPage(); });
};

La seconde variable isBottomPage va déterminer si un nouveau “scroll” de la page est possible ou pas. Cette opération est assurée en comparant la hauteur de la page entre le dernier et l’avant dernier “scroll” (scrollDelta).

Si la différence est différente de zéro, alors notre variable scrollPage est appelée de nouveau (et ainsi de suite). Dans le cas contraire, nous allons récupérer les titres des produits avec la fonction getTitles() que nous écrirons par la suite.

var isBottomPage = function() {
casper.then(function() {
if (scrollDelta != 0) {
scrollPage();
} else {
casper.then(function() {
this.wait(1000, function() {
titles = this.evaluate(getTitles);
});
});
}
});
};

Récupérer les titres des produits

Comme dans le projet “Google”, nous allons faire appel à une fonction permettant de récupérer les éléments contenus sur la page. Cette fois-ci la fonction getTitles() va récupérer tous les sélecteurs ‘.content_dc5c5 .title_9ddaf’ qui correspondent aux titres et retourner un tableau avec les éléments texte, grâce à la fonction innerHTML.

function getTitles() {
var titles = document.querySelectorAll('.content_dc5c5 .title_9ddaf');
return Array.prototype.map.call(titles, function(e) {
return e.innerHTML;
});
};

Initier le script

Dans un premier temps, nous allons charger la page, grâce à la fonction start(), et afficher son titre avec la fonction getTitle() (à ne pas confondre avec notre fonction getTitles() !).

casper.start(url, function() {
this.echo(this.getTitle());
});

Ensuite, nous allons lancer notre fonction scrollToBottom() afin de charger l’intégralité des produits de la page.

scrollToBottom();

Enfin, nous lançons l’exécution du script avec la fonction run() et affichons les résultats avec la fonction echo(). Attention, lorsque nous initions un callback après une fonction run(), il est nécessaire de terminer les instructions avec la fonction exit().

casper.run(function() {
this.echo(titles.length + ' produits trouvés:');
this.echo(' - ' + titles.join('\n - ')).exit();
});

Lancer le script

~ casperjs producthunt.js

Le résultat attendu 🤖 :

Wearables topic on Product HuntHauteur de la page :  3077Hauteur de la page :  5361Hauteur de la page :  5492Hauteur de la page :  7769Hauteur de la page :  7880Hauteur de la page :  788061 produits trouvés:- Airdog 2- Hykso 2.0- Dreem[...]- Lumo Lift
Tada !

Code complet :

var casper = require("casper").create({
verbose: true,
logLevel: 'error',
pageSettings: {
loadImage: false,
loadPlugins: false,
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36'
}
});
var url = 'https://www.producthunt.com/topics/wearables?subtopic=232';
var titles = [];
var scrolled = 0;
var scrollDelta = null;
var scrollPage = function() {
casper.wait(1000, function() {
casper.scrollToBottom();
var newScrolled = casper.evaluate(function() {
return window.scrollY; // Mesurer la hauteur de la page
});
scrollDelta = newScrolled - scrolled;
scrolled = newScrolled;
console.log("Hauteur de la page : ", scrolled);
isBottomPage();
});
};
var isBottomPage = function() {
casper.then(function() {
if (scrollDelta != 0) {
scrollPage();
} else {
casper.then(function() {
this.wait(1000, function() {
titles = this.evaluate(getTitles);
});
});
}
});
};
function getTitles() {
var titles = document.querySelectorAll('.content_dc5c5 .title_9ddaf');
return Array.prototype.map.call(titles, function(e) {
return e.innerHTML;
});
};
casper.start(url, function() {
this.echo(this.getTitle());
});
scrollPage(); casper.run(function() {
this.echo(titles.length + ' produits trouvés:');
this.echo(' - ' + titles.join('\n - ')).exit();
});

Si vous souhaitez vous former au JavaScript auprès de passionnés, nous proposons des formations au développement web (React JS) ou mobile (React Native). N’hésitez pas à visiter LeReacteur.IO

--

--

Kévin Lemaire
Le Reacteur

Co-fondateur de LeReacteur.IO et co-organisateur des Meetup React Lovers