Skip to content
This repository has been archived by the owner on Mar 4, 2020. It is now read-only.

AntoineOrgerit/Web-Scrapping

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

33 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Lisa FOUGERON - François GRÉAU - Antoine ORGERIT

TD 1 WebScraping

Notes :

L'écriture de ce rapport ayant débuté avant la formation du groupe, le code utilisé pour l'exercice 1 diffère de celui pour les autres exercices, ainsi que pour BoilerPipe.

Si vous désirez exécuter notre code, pensez à adapter les chemins.

Exercice 1 : Utilisation d'outils de détourage

Nous nous basons sur le dossier html fourni, possédant les caractéristiques suivantes :

  • 1694 fichiers, totalisant 127 002 561 caractères en 2 345 719 lignes
  • 129 Mo.

On veut utiliser trois outils afin d'extraire le contenu des articles de ces fichiers. Pour ce faire, Nous avons développé un code en python permettant de créer des fichiers contenant les résultats en spécifiant une méthode de scraping :

import glob
import os

# Parses through the given paths and applies the given scraping method
def parseFile(inputPath, outputPath, fileName, scrapingFunction):
    page = open(inputPath + fileName, encoding="utf-8", errors="ignore")
    pageContent = page.read()
    page.close()

    output = open(outputPath + fileName, "a", encoding="utf-8", errors="ignore")

    # Creates an empty file if the original file is empty
    if pageContent == "":
        output.write(" ")
    else:
        paragraphs = scrapingFunction(pageContent)
        for paragraph in paragraphs:
            if (paragraph.text):
                output.write("<p> " + paragraph.text.replace('\n', ' ') + " </p>\n")
        output.close()

# Parent folder and list of files 
folder = "D:/DocumentsHDD/M2/WebScraping/Corpus_detourage/" # À ADAPTER SI BESOIN
files = [f for f in glob.glob(folder + "html/*")]

# Placeholder for the future scraping methods
def scrapingMethodPlaceholder()

index = 1
for file in files: 
    os.system("cls")
    parseFile(folder + "html/", folder + "output/", file.split('\\')[-1], scrapingMethodPlaceholder)
    print("parsed " + str(index) + " out of " + str(len(files)) + " files")
    index += 1

Il ne reste plus qu'à définir des fonctions pour implémenter le fonctionnement des outils.

a. JusText

Pour le moment, nous n'identifions pas les langues dans lesquelles sont nos fichiers. L'exécution sera donc probablement plus lente et/ou moins fiable.

import justext

# JusText scraping method
def justextScraping(content):
    return justext.justext(content, "")

Résultat : le dossier généré pèse désormais 19,1 Mo.

b. BoilerPipe

from boilerpipe.extract import Extractor

# BoilerPipe scraping method
def boilerPipeScraping(content):
    extractor = Extractor(extractor="ArticleExtractor", html=content)
    return extractor.getHTML

Résultat : le dossier généré pèse désormais 7,41 Mo.

c. BeautifulSoup

from bs4 import BeautifulSoup

# Since the parseFile method requires objects with a text attribute
class Struct:
  def __init__(self, **entries):
    self.__dict__.update(entries)

# BeautifulSoup scraping method
def beautifulSoupScraping(content):
    soup = BeautifulSoup(content).stripped_strings
    out = []
    for elem in soup:
        out.append( Struct( **{"text": elem} ) )
    return out

Résultat : le dossier généré pèse désormais 53,5 Mo.

Comparaisons

Afin de calculer les métriques relatives au nombre de caractères et de lignes entre les résultats obtenus et la référence, nous avons écrit le script suivant en python :

import os
import math

def calculateAverage(referenceFolder, targetFolder):
    path = "D:/DocumentsHDD/M2/WebScraping/Corpus_detourage/"

    nbFiles = len(os.listdir(path + referenceFolder))

    initialCharNumber = 0
    initialLineNumber = 0
    folderCharNumber = 0
    folderLineNumber = 0
    varianceChar = 0
    varianceLine = 0

    for fileName in os.listdir(path + referenceFolder):

        # Reference folder
        currentFile = open(path + referenceFolder + "/" + fileName, encoding="utf-8", errors="ignore")
        content = currentFile.read()
        currentFile.close()
        initialCharNumber += len(content)
        initialLineNumber += content.count('\n')

        # Target folder
        currentFileTarget = open(path + targetFolder + "/" + fileName, encoding="utf-8", errors="ignore")
        contentTarget = currentFileTarget.read()
        currentFileTarget.close()
        folderCharNumber += len(contentTarget)
        folderLineNumber += contentTarget.count('\n')

        varianceChar += pow(len(content) - len(contentTarget), 2)
        varianceLine += pow(content.count('\n') - contentTarget.count('\n'), 2) 

    print("\nb total de caractères du dossier " + referenceFolder + " : " + str(initialCharNumber))
    print("nb total de lignes du dossier " + referenceFolder + " : " + str(initialLineNumber))
    
    print("\nnb total de caractères du dossier " + targetFolder + " : " + str(folderCharNumber))
    print("nb total de lignes du dossier " + targetFolder + " : " + str(folderLineNumber))

    print("\ndifférence moyenne de caractères : " + str((initialCharNumber - folderCharNumber) / nbFiles) )
    print("différence moyenne de lignes : " + str((initialLineNumber - folderLineNumber) / nbFiles) )

    print("\nvariance caractères : " + str(varianceChar / nbFiles) + " -> Ecart type caractères : " + str(math.sqrt(varianceChar / nbFiles)))
    print("variance lignes : " + str(varianceLine / nbFiles) + " -> Ecart type lignes : " + str(math.sqrt(varianceLine / nbFiles)))

# Attention ! Ne pas itérer à partir des fichiers de "clean" car il y a des fichiers en trop
calculateAverage("JT", "clean")

Grace à cela, nous relevons les données suivantes :

JT BP BS
Nombre de caractères 14 659 367 5 956 979 49 504 518
Nombre de lignes 306 757 26 828 527 095
Moyenne de la différence du nombre de caractères 6 370.41 1 359.62 26 931.97
Moyenne de la différence du nombre de lignes 166.6 9.52 296.71
Écart type du nombre de caractères 5 907.68 1 943.35 32 151.27
Écart type du nombre de lignes 125.18 13.42 214.78

En plus :

Afin d'observer plus facilement l'efficacité des outils, nous avons eu l'idée de générer un graphe décrivant la différence du nombre de caractères de chaque fichier entre les résultats de la vérité terrain et des méthodes de scraping :

  • JusText

difference_clean_jt

Nous pouvons alors facilement observer que mis à part de rares extrêmes, les différences sont relativement faibles.

  • BoilerPipe

difference_clean_bp

Dans ce cas, c'est encore plus flagrant : la différence en terme de nombre de caractères est très satisfaisante.

  • BeautifulSoup

difference_clean_bs

Ici, c'est un peu plus médiocre : la différence grimpe en flèche, et les cinq derniers pourcents sont très éloignés de ce que l'on attend.

Exercice 2 : Guider le scraping avec la reconnaissance de langue

Afin d'accélérer l'exécution de JusText, on veut connaître la langue de chaque fichier afin de l'indiquer lors du scraping. Pour ce faire, deux choix s'offrent à nous. On peut utiliser la librairie langid.py :

def jt_langid_treatement(input_file, output_file):
    if input_file.read() != " ":
        input_file.seek(0)
        language = langid.classify(input_file.read())
        language = languages.get(alpha2=language[0]).name
        
        # Greek is the only poorly formatted one so we convert "Modern Greek (1453-)" into "Greek"
        if "Greek" in language:
            language = "Greek"
        # If language is unspecified, we choose English
        if language not in justext.get_stoplists():
            language = "English"
        
        input_file.seek(0)
        paragraphs = justext.justext(input_file.read(), justext.get_stoplist(language))
        
        for paragraph in paragraphs:
            output_file.write("<p>" + paragraph.text.replace("\n", " ") + "</p>\n")
    else:
        output_file.write(" ")

...ou bien le fichier JSON trueLg qui nous est fourni :

def jt_truelg_treatement(input_file, output_file, file_name):
    if input_file.read() != " ":
        input_file.seek(0)
        languages = json.load(open("../../../resources/doc_lg.json"))
        
        language = languages[os.path.basename(file_name)]
        
        # If language is unspecified, we choose English 
        if language not in justext.get_stoplists():
            language = "English"
        
        paragraphs = justext.justext(input_file.read(), justext.get_stoplist(language))
        
        for paragraph in paragraphs:
            output_file.write("<p>" + paragraph.text.replace("\n", " ") + "</p>\n")
    else:
        output_file.write(" ")

En recalculant les métriques précédentes pour JusText, on obtient les valeurs suivantes :

Valeur pour langid / trueLg
Nombre de caractères 14 048 036
Nombre de lignes 306 755
Moyenne de la différence du nombre de caractères 6 011.14
Moyenne de la différence du nombre de lignes 166.79
Écart type du nombre de caractères 5 702.18
Écart type du nombre de lignes 125.18

On se rend compte que les valeurs sont similaires à celles obtenues sans préciser la langue. Nous supposons que la différence se fait plutôt dans la vitesse d'exécution.

Une bonne façon de s'en assurer est de comparer la vitesse d'exécution avec et sans la spécification de la langue au préalable. Pour cela, on relève le temps avant et après le traitement des 1694 fichiers.

  • Sans spécification :
def main():
    total = 0
    begin = time.time() # Début 
    extract("../../Corpus_detourage/Corpus_detourage/html/", "../../Corpus_detourage/Corpus_detourage/JT/", jt_treatement)
    end = time.time() # Fin
    total = (end-begin)
    print("Temps : "+ str(total))
  • Avec spécification :

Dans ce cas, il faut faire attention à ce que la détermination de la langue n'influe pas sur la durée de la mesure. Pour cela, nous avons choisi d'effectuer le test avec trueLg, puisqu'aller chercher la valeur dans le fichier JSON prend un temps infime.

def main():
    total = 0
    begin = time.time() # Début
    extract("../Corpus_detourage/Corpus_detourage/JT/", "../Corpus_detourage/Corpus_detourage/JTtrueLg/", jt_truelg_treatement)
    end = time.time() # Fin
    total = (end-begin)
    print("Temps : "+ str(total))

On relève les valeurs suivantes :

Langue Temps d'exécution en ms
Non spécifiée 87 199
Spécifiée 73 108

Notre hypothèse est vérifiée : même en allant chercher les langues dans un fichier JSON, spécifier la langue lors de l'appel à JusText réduit la durée d'exécution de manière substantielle.

Exercice 3 : Évaluation intrinsèque

  • Mesure des valeurs de la F-Mesures, du Rappel et de la Précision en fonction des langues

OUTILS
All el pl
F R P F R P F R P
JT 34.67 88.81 23.90 44.36 97.58 30.45 43.08 94.29 30.34
BP 57.20 64.30 53.41 81.04 89.31 75.13 73.58 80.00 70.80
BS 21.14 88.44 13.32 23.43 97.49 14.83 30.37 94.20 19.48
JT_langid 38.91 92.89 27.38 48.14 96.61 34.04 46.11 93.10 33.26
JT_trueLg 38.91 92.89 27.38 48.14 96.61 34.04 46.11 93.10 33.26
OUTILS
ru en zh
F R P F R P F R P
JT 31.19 93.81 21.14 44.59 96.41 31.25 13.09 66.96 8.30
BP 59.14 70.13 54.89 76.57 84.54 71.54 6.02 9.21 4.73
BS 18.06 94.04 10.92 30.40 96.02 19.37 4.50 65.85 2.59
JT_langid 32.80 89.94 22.58 49.51 98.04 35.63 19.35 86.13 12.39
JT_trueLg 32.80 89.94 22.58 49.51 98.04 35.63 19.35 86.13 12.39
  • Mesure des valeurs de la F-Mesure, du Rappel et de la Précision en fonction des sources

OUTILS
All www.express.gr
F R P F R P
JT 34.67 88.81 23.90 53.00 97.64 37.06
BP 57.20 64.30 53.41 85.99 92.83 80.32
BS 21.14 88.44 13.32 5.40 97.73 2.79
JT_langid 38.91 92.89 27.38 57.64 97.45 41.67
JT_trueLg 38.91 92.89 27.38 57.64 97.45 41.67
OUTILS
goodcontents.net biolog.pl
F R P F R P
JT 54.86 97.67 38.75 63.92 98.10 48.56
BP 87.64 92.52 83.63 85.89 89.54 82.70
BS 49.65 99.13 33.54 42.77 98.21 28.19
JT_langid 57.63 96.18 41.75 65.97 97.33 51.06
JT_trueLg 57.63 96.18 41.75 65.97 97.33 51.06

Exercice 4 : Extension

On part désormais sur trois autres méthodes d'extraction, Unfluff, Victor et Body Text Extractor.

a. Unfluff

Afin d'utiliser Unfluff, nous avons écrit et exécuté le code javascript suivant :

var extractor = require('unfluff');
var fs = require('fs');

var source_files_path = '../../resources/html/';
var dest_files_path = '../../resources/unfluff/'
var files = fs.readdirSync(source_files_path);

files.forEach(function(file) {
    fs.readFile(source_files_path + file, function(err, content) {
        if (err) {
            console.log(err);
        } else {
            data = extractor(content)
            content_to_write = ' ';
            if (data.text != '') {
                content_to_write = data.text
            }
            fs.appendFile(dest_files_path + file, content_to_write, function(
                    err) {
                if (err) {
                    console.log(err);
                }
            });
        }
    });
});

b. Victor

Malgré nos efforts et une installation réalisée en suivant scrupuleusement la documentation, nous n'avons pas réussi à faire fonctionner Victor.

c. Body Text Extractor

L'utilisation de BTE est très similaire à celle de JusText, aussi nous n'allons pas la détailler en profondeur.

Comparaisons

UF BTE
Nombre de caractères 1 784 829 5 946 865
Nombre de lignes 20 475 3 388
Moyenne de la différence du nombre de caractères 1 413.07 1 906.36
Moyenne de la différence du nombre de lignes 12.60 12.48
Écart type du nombre de caractères 1842.42 4316.50
Écart type du nombre de lignes 14.68 9.75
  • Relevé des F, R et P en fonction des langues :

OUTILS
All el pl
F R P F R P F R P
UF 35.42 32.23 93.42 2.11 1.26 98.52 46.18 41.54 84.17
BTE 56.80 65.21 59.61 74.06 88.22 67.86 71.39 80.31 70.50
OUTILS
ru en zh
F R P F R P F R P
UF 2.15 1.14 97.06 83.40 81.88 88.17 16.03 8.87 100.00
BTE 55.04 68.67 55.75 70.02 82.34 70.38 20.93 17.05 36.56
  • Relevé des F, R et P en fonction des sources :

OUTILS
All www.express.gr
F R P F R P
UF 35.42 32.23 93.42 2.75 2.00 99.95
BTE 56.80 65.21 59.61 90.00 94.48 86.08
OUTILS
goodcontents.net biolog.pl
F R P F R P
UF 90.84 93.70 88.38 61.02 55.72 89.15
BTE 61.63 96.07 45.97 87.76 86.74 88.88

Exercice 5 : Évaluation extrinsèque

Le code Daniel a été adapté pour être utilisé par notre code personnel et pour correspondre à notre cas d'utilisation. Les changements effectués ont été spécifié aux débuts des fichiers correspondants et à l'endroit même où les modifications ont été effectuées le cas échéant. La méthode de vérification getResults du fichier evaluate.py a notamment été modifiée pour prendre en paramètre une fonction d'extraction de critère de recherche au lieu de directement effectuer le processus par langage, ce qui permet de mettre en place une évaluation par sources.

On observe des résultats très différents entre les outils, probablement lié au contenu extrait lors des exercices précédents qui donne un contexte plus ou moins propice à la reconnaissance de maladies ou de localisations par Daniel.

  • Relevé des TP, FP, FN et TN :

OUTILS
TP FP FN TN
JT 8 1 116 1 569
BP 0 0 124 1 570
BS 76 48 48 1 522
JT_langid 8 1 116 1 569
JT_trueLg 8 1 116 1 569
UF 41 56 83 1 514
BT 85 147 39 1 423
  • Relevé des F, R et P en fonction des langues :

OUTILS
All en cn
F R P F R P F R P
JT 0.12 0.06 0.89 0.33 0.20 0.88 0.00 0.00 0.00
BP 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
BS 0.61 0.61 0.61 0.54 0.60 0.49 0.00 0.00 0.00
JT_langid 0.12 0.06 0.89 0.33 0.20 0.88 0.00 0.00 0.00
JT_trueLg 0.12 0.06 0.89 0.33 0.20 0.88 0.00 0.00 0.00
UF 0.37 0.33 0.42 0.52 0.86 0.38 0.00 0.00 0.00
BTE 0.48 0.69 0.37 0.35 0.74 0.23 0.00 0.00 0.00
OUTILS
ru el pl
F R P F R P F R P
JT 0.07 0.03 1.00 0.00 0.00 0.00 0.00 0.00 0.00
BP 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
BS 0.73 0.76 0.71 0.59 0.59 0.59 0.77 0.85 0.70
JT_langid 0.07 0.03 1.00 0.00 0.00 0.00 0.00 0.00 0.00
JT_trueLg 0.07 0.03 1.00 0.00 0.00 0.00 0.00 0.00 0.00
UF 0.00 0.00 0.00 0.00 0.00 0.00 0.50 0.41 0.65
BTE 0.62 0.86 0.48 0.53 0.76 0.41 0.69 0.78 0.62
  • Relevé des F, R et P en fonction des sources

OUTILS
All ibnlive.in.com
F R P F R P
JT 0.12 0.06 0.89 0.00 0.00 0.00
BP 0.00 0.00 0.00 0.00 0.00 0.00
BS 0.61 0.61 0.61 0.67 1.00 0.50
JT_langid 0.12 0.06 0.89 0.00 0.00 0.00
JT_trueLg 0.12 0.06 0.89 0.00 0.00 0.00
UF 0.37 0.33 0.42 0.00 0.00 0.00
BTE 0.48 0.69 0.37 0.00 0.00 0.00
OUTILS
www.bbc.co.uk www.abc.net.au
F R P F R P
JT 0.00 0.00 0.00 0.00 0.00 0.00
BP 0.00 0.00 0.00 0.00 0.00 0.00
BS 0.80 1.00 0.67 0.67 1.00 0.50
JT_langid 0.00 0.00 0.00 0.00 0.00 0.00
JT_trueLg 0.00 0.00 0.00 0.00 0.00 0.00
UF 0.40 0.50 0.33 1.00 1.00 1.00
BTE 0.50 1.00 0.33 0.67 1.00 0.50

About

Web scrapping of websites to extract information. Evaluation and comparison of different tools (BoilerPipe, JusText...).

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published