Passer

Common-Crawl Fouille de données Common-crawl avec ElasticSearch et Kibana

2020-09-Fouille de données Common-crawl avec ElasticSearch et Kibana

Dans notre dernier article, nous avons vu comment extraire les données brutes des pages du Common-crawl et à partir des données de ces pages, nous avons produit quelques statistiques portant sur les langues utilisées dans les pages, les domaines internet des pages ainsi que sur la localisation (niveau pays) des serveurs d’où elles sont publiées.

 

Aujourd’hui, nous proposons de reprendre les données téléchargées et de constituer un corpus de textes. Nous indexerons ensuite ces données en “text intégral” (full-text en anglais) à l’aide du moteur ElasticSearch. Après cela, nous utiliserons l’application Kibana pour explorer en langage naturel les pages web enregistrées dans ElasticSearch.

Repository git de référence

https://github.com/catherineverdiergo/cc-examples

Préparation de l’environnement

Installation de Apache Spark

  • Installez un JDK1.8 et définissez la variable d’environnement JAVA_HOME,
  • Ajoutez également $JAVA_HOME/bin dans votre PATH,
  • Téléchargez la dernière archive Spark_2.4 depuis
    https://downloads.apache.org/spark/spark-2.4.5/spark-2.4.5-bin-hadoop2.7.tgz,
  • Décompressez-la dans le path de votre choix que vous réutiliserez pour définir la variable d’environnement SPARK_HOME.

Installation de l’environnement Python

Nous travaillerons avec un environnement virtuel Anaconda de type biopython que nous allons enrichir avec quelques paquets supplémentaires.

 

$> conda create -n cc-pyspark biopython python=3.7

$> conda activate cc-pyspark

# Ajout des paquets supplémentaires

(cc-pyspark) $> pip install requests

(cc-pyspark) $> pip install warc3-wet

(cc-pyspark) $> pip install lang-id

(cc-pyspark) $> pip install maxminddb-geolite2

(cc-pyspark) $> pip install jupyter

(cc-pyspark) $> pip install plotly

(cc-pyspark) $> pip install pandas

 

Description des paquets additionnels :

 

paquet

description

requests

Pour dialoguer en https

warc3-wet

Pour parser les fichiers WARC ou WET

lang-id

Pour identifier la langue d’un texte

maxminddb-geolite2

Pour identifier la géolocalisation d’un serveur

jupyter

Pour travailler avec Jupyter notebooks

matplotlib + seaborn

Pour créer des graphiques dans un notebook

pandas

Dataframes Python exploitables avec plotly

 

Installer et lancer ElasticSearch

Deux modes d’installation : directement sur votre machine ou via docker 

Directement sur votre machine

Au moment où j’écris cet article, la dernière version disponible est la 7.8.0 et elle peut être téléchargée ici : https://www.elastic.co/fr/downloads/elasticsearch

  • Téléchargez l’archive correspondant à votre système d’exploitation,
  • L’installation est simple : décompressez l’archive à l’emplacement de votre choix sur le disque et optionnellement,
  • Créez-vous une variable d’environnement ES_HOME,
  • Lancez ElasticSearch avec : $ES_HOME/bin/elasticsearch (ajoutez .bat sous Windows),
  • Vérifiez que votre serveur est on-line en allant sur http://localhost:9200.

Avec Docker

docker run -p 9200:9200 -p 9300:9300 \

      -e « discovery.type=single-node » \

      –name elastic \

      docker.elastic.co/elasticsearch/elasticsearch:7.8.0

Comme expliqué ici : https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html

Vérifiez ensuite que la page http://localhost:9200 réponde bien.

 

Installer et lancer kibana

Les versions ElasticSearch et Kibana doivent toujours être synchronisées.

Nous allons donc installer Kibana7.8.0.

Deux modes d’installation : directement sur votre machine ou via docker 

Directement sur votre machine

  • Téléchargez l’archive https://www.elastic.co/fr/downloads/kibana et procéder de la même façon qu’avec ElasticSearch,
  • Lancez Kibana avec $KIBANA_HOME/bin/kibana(.bat),
  • Vérifiez que Kibana fonctionne correctement en allant sur : http://localhost:5601

Avec Docker

Retrouvez l’ID de votre container ElasticSearch avec un :

docker ps

puis lancez :

docker run \

      –link elastic:elasticsearch \

      -p 5601:5601 docker.elastic.co/kibana/kibana:7.8.0

 

Vérifiez ensuite que la page http://localhost:5601 réponde bien.

 

Vous pouvez télécharger les données exemples ou non. Bien entendu, à ce stade, notre index ElasticSearch n’est pas encore alimenté.

 

Install elasticsearch/spark

  • Téléchargez la version ad-hoc de elasticsearch-hadoop depuis :
    https://www.elastic.co/fr/downloads/hadoop,
  • Depuis l’archive téléchargée, extrayez la lib java suivante : elasticsearch-spark-20_2.11-7.8.0.jar et enregistrez la dans le répertoire de votre choix.

La bibliothèque elasticsearch-spark-20_2.11-7.8.0.jar va nous servir à alimenter un index ElasticSearch.

 

Quelques mots sur ElasticSearch

ElasticSearch est une base de données scalable orientée document.

ElasticSearch, comme le produit concurrent le plus célèbre : Apache SolR, embarque la bibliothèque Java Lucene. Lucene permet de constituer des index inversés à partir d’un corpus de textes.

En mode par défaut, lors de l’insertion de données, ElasticSearch procède (via lucene) à une indexation de tous les champs texte d’un document, ce qui permet d’effectuer des recherches full-text dans les documents.

 

Bien qu’il s’agisse d’une base de données, l’unité de stockage d’entités dans ElasticSearch ne s’appelle pas “table” mais “index”. Un index Lucene ou ElasticSearch est en fait une table de stockage accompagnée de son index full-text.

 

Extraction d’un corpus prêt à indexer

Choix des langues

ElasticSearch est nativement doté de modules permettant d’effectuer un preprocessing sur les textes d’un corpus avant d’effectuer son indexation.

Ce preprocessing correspond au travail de l’analyzer associé à l’index.

Le moteur dispose d’un analyzer par défaut (que nous allons utiliser pour simplifier), mais il est possible :

 

Par contre, il n’est pas possible d’utiliser plusieurs analyzers pour traiter un seul corpus.

Ainsi, si l’on souhaite spécialiser ses analyzers par langue, il faudra également prévoir un index par langue lors de l’intégration des données.

 

Comme nous souhaitons utiliser l’analyzer par défaut, pour obtenir des résultats assez corrects, nous allons extraire de nos fichiers wet issus de Common Crawl un corpus composé uniquement de textes en anglais et en français.

 

Pour en savoir plus sur le preprocessing opéré par l’analyzer par défaut d’ElasticSearch, consultez : https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-standard-analyzer.html

 

Programme d’extraction et résultats

Sur le modèle de l’article précédent, nous avons travaillé sur 4 fichiers WET pour extraire deux datasets en parquet contenant respectivement un corpus français et un corpus anglais.

Pour cette version, nous ne nous sommes pas contentés d’identifier seulement le pays d’hébergement du serveur de domaine mais nous avons également intégré des données plus fines de géolocalisation.

 

Voir le fichier python:

https://github.com/catherineverdiergo/cc-examples/blob/master/wetesindexcorpus.py

 

(cc-pyspark) $> PYTHONPATH=$SPARK_HOME/python:$SPARK_HOME/python/lib/py4j-0.10.7-src.zip:$PYTHONPATH \

                  PYSPARK_DRIVER_PYTHON=`which python` \

                  PYSPARK_PYTHON=`which python` \

                  PYSPARK_SUBMIT_ARGS=”local[*] –driver-memory 4g” \

                  python wetesindexcorpus.py CC-MAIN-2020-16 for_es_index

 

L’exécution du programme étant assez longue, il est possible de travailler directement sur les corpus résultant, sauvegardé dans le répertoire :

https://github.com/catherineverdiergo/cc-examples/tree/master/for_es_indexing

 

Création du mapping d’index ElasticSearch

Avant d’intégrer nos données, nous allons créer un mapping pour notre nouvel Index.

On peut considérer cela comme un schéma des données : on pourra y préciser quels champs seront ou non indexés (au sens full-text) et éventuellement y définir un analyzer personnalisé (nous ne le ferons pas sur cet exemple puisque nous avons décidé de nous fier à l’analyzer par défaut).

 

Nous allons construire notre mapping en nous basant sur le schéma parquet de nos fichiers de sortie. Nous pouvons l’afficher aisément en exécutant les instructions suivantes avec pyspark:

>>> df=spark.read.parquet(‘for_es_indexing’)

>>> df.printSchema()

root

 |– url: string (nullable = true)

 |– domain: string (nullable = true)

 |– date: date (nullable = true)

 |– country: string (nullable = true)

 |– loc: array (nullable = true)

 |    |– element: float (containsNull = true)

 |– text: string (nullable = true)

 |– lang: string (nullable = true)

 

Pour construire notre mapping ElasticSearch, nous construisons un template ElasticSearch que nous allons intégrer dans le moteur à l’aide de l’API Restful ElasticSearch et de Kibana.

 

Voici notre template :

 « order » : 0,

 « index_patterns » : [ « index_cc_us_en* » ],

 « settings » : 

    { 

        « index » : { « number_of_shards » : « 1 », « number_of_replicas » : « 0 » } },

        « mappings » : 

           { 

             « _source » : { « enabled » : true },

             « properties » : 

                 { 

                     « url » : { « type » : « text » },

                     « domain » : { « type » : « keyword » },

                     « date » : { « type » : « date » },

                     « country » : { « type » : « keyword » },

                     « loc » : { « type » : « geo_point » },

                     « text » : { « type » : « text » },

                     « lang » : { « type » : « keyword » } 

                 }

           },

  « aliases » : { }

}

 

Et voici quelques éléments pour comprendre comment il est construit :

 

img1-2

Pour enregistrer le template sous ElasticSearch, affichez la page Kibana/Dev-tools : http://localhost:5601/app/kibana#/dev_tools/console, et entrez la commande REST suivante :

 

PUT _template/cc_tpl_1

 « order » : 0,

 « index_patterns » : [ « index_cc_us_en* » ],

 « settings » : 

    { 

        « index » : { « number_of_shards » : « 1 », « number_of_replicas » : « 0 » } },

        « mappings » : 

           { 

             « _source » : { « enabled » : true },

             « properties » : 

                 { 

                     « url » : { « type » : « text » },

                     « domain » : { « type » : « keyword » },

                     « date » : { « type » : « date » },

                     « country » : { « type » : « keyword » },

                     « loc » : { « type » : « geo_point » },

                     « text » : { « type » : « text » },

                     « lang » : { « type » : « keyword » } 

                 }

           },

  « aliases » : { }

}

Ensuite exécutez-la à l’aide de la flèche sur la droite du panel (située sur la même ligne que l’instruction REST “PUT _template/cc_tpl_1”).

Enregistrement des données dans un Index

Cela peut se faire facilement avec pyspark et la bibliothèque java elasticsearch-spark-20_2.11-7.8.0.jar que nous avons téléchargée précédemment.

 

  1. Placez-vous dans le répertoire du projet,
  2. Lancez pyspark via la commande suivante :

pyspark –jars $path_to_your_libs/elasticsearch-spark-20_2.11-7.8.0.jar \

        –driver-memory 4g

  1. Puis, dans pyspark, tapez les deux commandes suivantes :

df = spark.read.parquet(‘for_es_indexing’)

(df.write 

       .mode(« overwrite »)

       .format(« org.elasticsearch.spark.sql »)

       .option(« es.nodes.wan.only », »true »)

       .option(« es.nodes », « http://localhost:9200 »)

       .save(« index_cc_us_en_20200630 »))

 

Pendant que le job traite les données, il est possible de voir le nombre de documents traité depuis l’interface Kibana :
http://localhost:5601/app/kibana#/management/elasticsearch/index_management/indices

 

Exploration du corpus avec Kibana Discover

Création d’un index pattern

Pour explorer un ou plusieurs index avec Kibana Discover, il faut préalablement créer un “index-pattern” :

 

Allez sur la page web suivante de kibana :

http://localhost:5601/app/kibana#/management/kibana/index_pattern?_g=()

et créez un index pattern avec le pattern suivant: « index_cc_us_en_*”

 

img2-1

A l’étape suivante, sélectionnez le champ “date” comme référence temporelle :

img3-1

Puis validez en créant votre index-pattern.

Explorer avec Kibana Discover

Allez à la page Discover de Kibana : http://localhost:5601/app/kibana#/discover

 

Sélectionnez votre index-pattern et réglez l’intervalle de temps à 1 an pour voir apparaître les premiers documents indexés :

 

img4-1

Sur l’histogramme de Kibana Discover, nos documents sont tous rassemblés à la même date : le 23 mars 2020 (date du crawl de Common Crawl) :

 

img5-1

 

Il est maintenant possible d’explorer notre corpus en entrant des requêtes full-text, par exemple :

 

img6-1

 

Si vous tentez plusieurs types de recherche, vous pouvez constater que le texte contenu dans les fichiers WET ne contient certes plus la partie HTML des pages crawlées mais que le texte résultant qui comporte beaucoup de “bruit” car tous les textes contenus dans les header/footer(têtes/pieds) des pages persistent dans le document texte résultant.

 

L’idéal serait sans doute d’appliquer un preprocessing préalable à l’intégration dans ElasticSearch en essayant d’éliminer des textes et/ou les paragraphes de moins de trois lignes par exemple.

 

Créez un dashboard avec une carte géographique sous Kibana

L’objectif est d’exploiter les données de géolocalisation intégrées dans le dataset afin d’avoir une idée de la répartition géographique des serveurs proposant les pages Web en français et en anglais que nous venons d’indexer.

 

Allez sur la page Kibana / Dashboard : http://localhost:5601/app/kibana#/dashboard

  • Cliquez sur le bouton “create new” et sélectionnez un object “Map”: ,
  • Repositionnez votre intervalle temporel d’exploration à 1 an (comme précédemment lors de l’usage de Kibana / Discover),
  • Cliquez ensuite sur votre droite (“Add layer”) et sélectionnez “Documents” :

img7-1

  • Sélectionnez ensuite l’index-pattern créé plus haut (le champ de type geo_point est automatiquement sélectionné car il est unique par document),
  • Sélectionnez enfin le radio-bouton “Show clusters when results exceed 10000”.

 

Vous obtenez alors une carte donnant la répartition géographique des serveurs d’où proviennent les pages de notre corpus.

 

On obtient une carte de ce type :

 

img8

Pour savoir comment mapper ces données de géolocalisation dans les index ElasticSearch (plusieurs méthodes sont possibles), référez-vous à :

https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-point.html

Conclusion

Cet article clôt ce cycle consacré au programme : “AWS Open Data Project”.

Nous espérons que ces petites publications vous auront donné soit envie de travailler avec les données publiques présentes dans ces projets, soit avoir des idées pour exploiter certains datasets et enrichir vos propres applications.