Ce paquet Node.JS scarppe les transactions d'une liste pour le Challenge Engineering Bankin' #1.
Fonctions principales | |
---|---|
☑️ | Récupère les 4999 transactions |
💯 | 100% fiable, peu importe le mode |
⚡ | Rapide (≈1.6s sur MacBook Pro 2017) |
✨ | Structure orientée objet, simple et propre |
📚 | 100% documenté |
-
Installez la dernière version de Google Chrome (testé avec 63.0)
-
Installez Node.JS (version minimum: 8.9.4 LTS)
-
Ouvrez un terminal, clonez ce répertoire et
cd
dans le dossier
git clone https://github.com/alexaubry/bankin-scraping alexaubry-bankin-scraping
cd alexaubry-bankin-scraping
-
Installez les dépendances Node (
npm install
) -
Lancez le script avec la commande
node .
Les transactions seront enregistrées dans le fichier transactions.json
à la racine du script.
- Écrit en JavaScript (ECMA 2017 - avec
async/await
) - Exécuté avec Node.JS
- Utilise Chrome Headless et Selenium pour le traitement de la page web
Le paquet n'a pas d'autres dépendances et se lance rapidement.
Le paquet contient deux composants essentiels:
- Le framework
scraping-kit
qui regroupe les algorithmes nécessaires au scraping - Le script
index.js
qui exécute les opérations
Le script a été développé après une analyse profonde du fonctionnement du site.
Cette analyse a révélé l'existence de trois modes:
-
Normal : un tableau ou une iFrame (choix aléatoire) sont affichés direcement au chargement du site
-
Lent : après quelques secondes (durée aléatoire), on bascule sur le mode normal
-
Échec : une alerte est affichée au lancement. Un bouton “Reload Transactions“ permet de basculer soit sur le mode normal, soit sur le mode slow (choix aléatoire)
Le mode est choisi aléatoirement au chargement du site.
Le site affiche un bouton "Next" qui charge la suite de la liste (ajoute le paramètre start
à l'URL). Toutefois cette URL pointe toujours vers la liste 100-150. Il faut donc ignorer ce bouton et gérer la pagination manuellement.
Si le paramètre start
dépasse le nombre total de transactions, le tableau affiché sera vide (avec seulement un header).
Bien que majoritairement illisible, la lecture du script load.js
a permi de découvrir des paramètres intéressants:
- Si
slowmode = true
alors le mode lent est activé - Si
failmode = true
alors le mode échec est activé - Si
hasiframe = true
alors le tableau sera affiché dans une iFrame - On peut modifier le début du tableau en manipulant la variable
start
-
generate()
: si le mode lent est activé, alors un délai d'une durée aléatoire est ajouté avant d'appelerdoGenerate()
. Sinon, la génération commence directement. -
doGenerate()
: génère les données et les affiche selon différents modes, en fonction des paramètres:- Si
hasiframe = true
, alors cette fonction va ajouter une nouvelle iframe et insérer un tableau dans la première iframe de la page. - Si
hasiframe = false
et qu'un tableau est déjà sur la frame princpale, ce tableau sera rechargé en prenant en compte le nouveaustart
- Si
hasiframe = false
et qu'il n'y a pas de tableau sur la frame princpale, un tableau sera ajouté
- Si
f5
: la limite du nombre total de transaction).
Il y a 4999 transactions en tout (le nombre de transactions devant être strictement inférieur à f5 [ligne 491]).
Le bouton Next ne permet donc pas d'atteindre toutes les transactions.
w5
: le nombre de transactions affichées dans un tableau. S'il avait été modifiable, il aurait été possible d'afficher plus d'éléments et potentiellement de gagner du temps.
La classe PageScraper
gère le scraping et retourne un Array de transactions une fois le scraping terminé.
Dans cette section, nous expliquerons le fonctionnement de cette classe.
Des alertes sont affichées aléatoirement au lancement. Il faut commencer par les cacher.
Une fois l'alerte cachée, deux options sont possibles:
- Détecter le mode actuel et faire en fonction (traiter chaque mode séparément)
- Ignorer le mode et normaliser la page (toujours afficher un tableau sur la frame principale)
Puisque la clarté du script est un critère déterminant, la seconde option semble plus pertinente. En effet, elle permet d'écrire une seule fonction de traitement du tableau et d'éviter des if
en cascade.
Pour normaliser la page il faut, dans le moteur JavaScript:
- Désactiver les modes lent (
slowmode
) et échec (failmode
) - Désactiver les iframe (
hasiframe
), pour que les données soient toujours affichées dans un tableau - Regénérer un tableau (
doGenerate()
)
Si on est en mode normal, doGenerate()
ne fera rien. En revanche, si on était en mode lent ou échec, cette méthode affichera directement un tableau sur la page, ce qui permettra de traiter rapidement les données et d'éliminer les problèmes.
Une fois un tableau affiché sur la page principale, on peut en extraire les données.
Une nouvelle fois, deux options semblent pouvoir être choisies:
- Utiliser Selenium pour récupérer les données depuis le DOM
- Exécuter un script dans le moteur JavaScript de Chrome avec les API DOM standard
La deuxième option s'est révélée être environ 150 (!) fois plus rapide. En effet, l'API de Selenium utilise des promesses et ne permet pas de regrouper les opérations, ce qui est possible avec l'API DOM de Chrome.
De plus, Selenium permet d'écécuter un script dans Chrome et de récupérer la valeur de retour, ce qui nous permet d'obtenir facilement le résultat du traitement fait depuis Chrome.
Dans la méthode parseTransactionData
, on extrait les données du tableau et ajoute la transaction à l'Array JSON.
Pour la pagination, plusieurs solutions étaient également envisageables:
- Changer le paramètre
start
dans l'URL et recharger la page - Utiliser JavaScript pour recharger les données sans requête réseau
Pour des raisons évidentes de rapidité, la solution 2 s'impose. De plus, l'utilisation de la fonction doGenerate()
lors de la normalisation permet de recharger rapidement le tableau et de réduire au maximum la latence entre le traitement de chaque page.
Le scraping s'articule donc de la manière suivante:
Tant que toutes les pages n'ont pas été traitées
- changer le numéro de page (variable `start`)
- normaliser la page et recharger les données
- extraire les données
- ajouter les données au résultat global
-
Un objet
PageLoader
configure Google Chrome et charge la page d'accueil dans un Driver -
On créé un objet
ScriptEvaluator
pour calculer la durée du scraping après le chargement de la page web -
Avec le driver retourné par le
PageLoader
, on créé unPageScraper
, qui retournera la liste des transactions -
La fonction
ResultsHandler.exportTransactions
convertit les transactions au format JSON et écrit le fichier sur le disque -
On affiche un message de succès et la durée totale du scraping
Paquet écrit par Alexis Aubry. Mis à disposition sous les termes de la licence MIT.