SummarizingBot

De Wikipast
Sauter à la navigation Sauter à la recherche

SummarizingBot est un bot programmé en langage Python et qui agit sur des pages Wikipast.

Description

Le SummarizingBot crée un résumé, lisible, pour différents personnages sur la base des données saisies dans la partie Biographie de la page Wiki de chaque personnage (tous les personnages traités se trouvent dans la page Wiki Biographies). Le volume approximatif du résume est de 200 mots. La taille du résume dépend fortement des qualités des données d'origine, acquises de la page du personnage. C'est-à-dire, leur volume (au moins 15 entrées) et l'exactitude de l'entrée (syntaxes du Wiki doivent être respectés).

Le principe clé du bot est l'analyse des données (entrées) en utilisant le concept de Expression Régulière (Regular Expression ou RegExp) pour identifier les différentes parties d’entrée (une ligne de donnée). Grâce à cette analyse, on va obtenir des blocs d'information à partir de laquelle chaque entrée est consisté. Ces blocs sont les suivants : Le bloc du Data, Le bloc de la Ville et, finalement, Le bloc d’information sur l'évènement. Ensuite, on tire les informations supplémentaires de chaque bloc et on les traite avec de différentes fonctions. En ce moment-là, on va comprendre quel type de données on a dans le bloc. Si le bloc est de bonne structure on va continuer à le traiter, sinon on va éliminer ce bloc. On peut facilement appliquer les fonctions pour chaque cas particulier des données dans le bloc pour créer une structure correcte applicable au résume.

La performance du code est telle qu'environ 92,8% de données du totale sont traitées et utilisées pour créer le résume. Ce pourcentage a été calculé comme le rapport entre le nombre total de lignes correctement lues par le code et le nombre total de lignes lues par le code de tous les pages de la Biographie.

Expression Régulière

^\s?(\d{4}\.?\d{0,2}\.?\d{0,2})(\s?\/\s?)?(([\w\s\-,]+))?\.?\s?(.+)$

Notre modèle de RegExp définit trois blocs principaux par motifs (patterns) suivantes :

  • Date
 ^\s?(\d{4}\.?\d{0,2}\.?\d{0,2})
  • Lieu (Ville)
(([\w\s\-,]+))?
  • Information (Événement)
s?(.+)

Datum Vérification

On veut fournir quelques exemples du traitement des lignes de données.

[1993.05.23][][] ---> "False", car on a une ligne vide par rapport à l'information.
[1993.05.23][Milan][Information][Bloc d'information non attendent] ---> "False'', car on a trop de blocs de data.
[1993-1994][Milan][Information] ---> "False'', car on a une date qui ne respecte pas le RegExp pour la date (format pas bon). 
[1993.05.23][Milan11][Information] ou [1993.05.23][Quelque  part][Information] ---> "False'', car on a un lieu qui n'a pas un bon format (chiffres) ou il y a nombreux espaces dans le série. 

Supprimer des Répétitions

On utilise l'algorithme pour éliminer les répétitions de ville dans le résumé.

*[[1969.07.29]] / [[Maranello]]. [[Enzo Ferrari]] a abandonné son poste directeur général. Il a été remplacé par M. [[Francesco Bellicardi]], président de Weber, maison de carburateurs appartenant a [[FIAT]].

*[[1986.03.12]] / [[Maranello]]. [[Enzo Ferrari]] présente le nouveau modèle [[Ferrari F1-86]].

En résumé on a le suit (pas de ville après le deuxième expression):

Le 1969.07.29 Enzo Ferrari a abandonné son poste directeur général. Il a été remplacé par M. Francesco Bellicardi, président de Weber, maison de carburateurs appartenant a FIAT.  [13] a Maranello. Le 1986.03.12 Enzo Ferrari présente le nouveau modèle Ferrari F1-86. [14]

Points Critique des Performances

Les points critiques sont les suivants :

  • Erreurs dans la saisie des données dans la biographie. Non-respect de la syntaxe posée pour l'entrée dans la Biographie.
  • Problème de la langue utilisée. La langue française est rarement utilisée dans les réseaux de neurones artificiels (artificial neural network) qui peuvent créer du texte à partir de données qu'on a.
  • La grammaire française est assez compliquée. Ceci impose un grand nombre de fonctions pour avoir un texte lisible et grammaticalement correct.
  • Étude détaillé du type de données d'entrée pour améliorer les motifs. Pour obtenir un pourcentage plus faible de données perdues et augmenter la quantité d'informations obtenues à partir d’une entrée.

Points d'Améliorations

En premier mis en marche on a vu que le résumé et très grand (car on a presque réécrit le biographie) et le texte n'est pas de bonne qualité. On propose les solutions suivantes :

  • L'échelle d'importance. Différences niveau concernes les informations se trouve dans le ligne d'entrée.
  • Donner de la priorité aux lignes de donnes avec les mots de caractère spécial (les mots qui ont une structure spéciale d’utilisation dans la biographie, p.ex. naissance, mariage etc). Ce type de données va au premier niveau de priorité (plus important).
  • Utilise les entrés avec le hyper mots (le deuxième niveau de priorité).
  • N'inerte pas le ligne dans le résumé s'il n'y pas de référence. Identifier ces données à l'aide du SourceBot.
  • Essayer de trouver un bon algorithme de construire le texte, en anglais, à l'aide des réseaux de neurones artificiels. Le biographie va traduire par le TranslatorBot.
  • Dans le code on introduire une liste des villes, que on peut utiliser comme estimateur de la qualité d’information dans le ligne. (Possédera le problème car la ville doit être écrire avec respections d’écriture sur la langue maternelle de la ville).

Exemple

L'exemple basé sur la page de biographie Enzo Ferrari

Données initiales:

=Biographie=

*[[1899.02.18]] / [[Maranello]]. [[Naissance]] de [[Enzo Ferrari]]. Mais, en raison d'une terrible tempête de neige, l'enfant ne pourra être déclaré que deux jours plus tard a l'état civil. D'ou son [[anniversaire]] "officiel" le 20 février. [http://www.letempsarchives.ch/page/GDL_1988_02_23/2/article/4760402/enzo%20ferrari%20naissance]

*[[1943]] / [[Modène]], [[Maranello]]. [[Transfert]] de l'usine [[Ferrari]] de [[Modène]] à [[Maranello]]. [http://www.letempsarchives.ch/page/GDL_1988_02_23/2/article/4760402/enzo%20ferrari%20naissance] 

*[[1946]] / [[Maranello]]. [[Fondation]]  de [[manufacture automobiles]]  [[Ferrari Automobiles]] par M.[[Enzo Ferrari]]. [http://www.letempsarchives.ch/page/GDL_1988_02_23/2/article/4760402/enzo%20ferrari%20naissance]

*[[1947.05.11]] / [[Piacenza]]. Première course de la [[Ferrari 125 Sport]] (la première Ferrari de course) sur le circuit de [[Piacenza]]. [http://www.letempsarchives.ch/page/GDL_1988_02_23/2/article/4760402/enzo%20ferrari%20naissance]

*[[1948.10.24]] / [[Garde]]. [[Victoire]] de [[Ferrari]] en circuit au lac de Garde pour la premier fois. Le coureur était [[Giuseppe "Nino" Farina]]. [http://www.letempsarchives.ch/page/GDL_1988_02_23/2/article/4760402/enzo%20ferrari%20naissance]

*[[1952]]  [[Victoire]] de [[Ferrari]] le premier titre de champion du monde des conducteurs de [[Formula 1]] avec [[Alberto Ascari]], sur [[Ferrari 500 F2/4 cyl.]].   [http://www.letempsarchives.ch/page/GDL_1988_02_23/2/article/4760402/enzo%20ferrari%20naissance]

*[[1959.09.11]] / [[Maranello]].  [[Annonce]] de M.[[Enzo Ferrari]] que "La firme automobile [[Ferrari]] n'est pas en vente". [http://www.letempsarchives.ch/page/JDG_1959_09_11/4/article/7896662/%22enzo%20ferarri%22]

*[[1959.12.30]] / [[Maranello]]. Au cours d'une conférence de presse, M.[[Enzo Ferrari]] envisageait le déplacement a [[Bologne]] des secteurs commercial, administratif et d'assistance sociale de ses établissements. [http://www.letempsarchives.ch/page/JDG_1959_12_30/11/article/7921746/%22enzo%20ferarri%22]

*[[1966.12.15]] / [[Maranello]]. [[Annonce]] de M. [[Enzo Ferrari]] que "L'an prochain, nous serons présents sur tous les fronts, y compris la formule deux". [http://www.letempsarchives.ch/page/JDG_1966_12_15/13/article/8004297/%22enzo%20ferarri%22]

*[[1967.06.22]] / [[Maranello]]. [[Enzo Ferrari]] frappe contre les nouvelles règles de la course "[[24 Heures du Mans]]". Si nouveau règles va accepte, il ne participera plus a cette épreuve. [http://www.letempsarchives.ch/page/JDG_1967_06_22/10/article/8037412/%22enzo%20ferarri%22]

*[[1967.08.18]] / [[Maranello]]. [[Annonce]] de M. [[Enzo Ferrari]] que "Après la mort tragique de [[Lorenzo Bandini]], j'ai décidé de limiter a l'avenir l'activité technique et sportivité de [[Ferrari]]". [http://www.letempsarchives.ch/page/JDG_1967_08_18/8/article/8046595/%22enzo%20ferarri%22]

*[[1969.06.23]] / [[Turin]]. [[Annonce]] du communiqué de la firme automobile [[FIAT]], que un accord de participation paritaire a été signé entre M.[[Enzo Ferrari]] et M. [[Giovanni Agnelli]].  [http://www.letempsarchives.ch/page/JDG_1969_06_23/6/article/8184906/%22enzo%20ferarri%22]

*[[1969.07.29]] / [[Maranello]]. [[Enzo Ferrari]] a abandonné son poste directeur général. Il a été remplacé par M. [[Francesco Bellicardi]], président de Weber, maison de carburateurs appartenant a [[FIAT]].  [http://www.letempsarchives.ch/page/GDL_1969_07_30/9/article/3446427/%22enzo%20ferarri%22]

*[[1986.03.12]] / [[Maranello]]. [[Enzo Ferrari]] présente le nouveau modèle [[Ferrari F1-86]]. [http://www.letempsarchives.ch/page/JDG_1986_03_12/27/article/9128502/%22enzo%20ferarri%22]

*[[1988.02.18]] / [[Maranello]]. [[Enzo Ferrari]] fête ses 90 ans. [http://www.letempsarchives.ch/page/GDL_1988_02_23/2/article/4760402/enzo%20ferrari%20naissance]

*[[1988.08.14]] / [[Maranello]]. [[Décès]] de [[Enzo Ferrari]]. [http://www.letempsarchives.ch/page/JDG_1988_08_16/1/article/9420659/%22enzo%20ferarri%22]

Résume :

=Résume=

Le 1899.02.18 Naissance de Enzo Ferrari. Mais, en raison d'une terrible tempête de neige, l'enfant ne pourra être déclaré que deux jours plus tard a l'état civil. D'ou son anniversaire "officiel" le 20 février. [1] a Maranello. En 1943 Transfert de l'usine Ferrari de Modène à Maranello. [2]  a Modène, Maranello. En 1946 Fondation  de manufacture automobiles Ferrari Automobiles par M.Enzo Ferrari. [3] a Maranello. Le 1947.05.11 Première course de la Ferrari 125 Sport (la première Ferrari de course) sur lecircuit de Piacenza. [4] a Piacenza. Le 1948.10.24 Victoire de Ferrari en circuit au lac de Garde pour la premier fois. Le coureur était Giuseppe "Nino" Farina. [5] a Garde. Le 1959.09.11  Annonce de M.Enzo Ferrari que "La firme automobile Ferrari n'est pas en vente". [7] a Maranello. Le 1959.12.30 Au cours d'une conférence de presse, M.Enzo Ferrari envisageait le déplacement a Bologne des secteurs commercial, administratif et d'assistance sociale de ses établissements. [8]. Le 1966.12.15 Annonce de M. Enzo Ferrari que "L'an prochain, nous serons présents sur tous les fronts, y compris la formule deux". [9]. Le 1967.06.22 Enzo Ferrari frappe contre les nouvelles règles de la course "24 Heures du Mans". Si nouveau règles va accepte, il ne participera plus a cette épreuve. [10]. Le 1967.08.18 Annonce de M. Enzo Ferrari que "Après la mort tragique de Lorenzo Bandini, j'ai décidé de limiter a l'avenir l'activité technique et sportivité de Ferrari". [11]. Le 1969.06.23 Annonce du communiqué de la firme automobile FIAT, que un accord de participation paritaire a été signé entre M.Enzo Ferrari et M. Giovanni Agnelli.  [12] a Turin. Le 1969.07.29 Enzo Ferrari a abandonné son poste directeur général. Il a été remplacé par M. Francesco Bellicardi, président de Weber, maison de carburateurs appartenant a FIAT.  [13] a Maranello. Le 1986.03.12 Enzo Ferrari présente le nouveau modèle Ferrari F1-86. [14]. Le 1988.02.18 Enzo Ferrari fête ses 90 ans. [15]. Le 1988.08.14 Décès de Enzo Ferrari. [16].

Code

from urllib.request import urlopen
import requests
from urllib.parse import quote
from bs4 import BeautifulSoup
from collections import OrderedDict
from geotext import GeoText
import re


# Login information
user='testbot'
passw='dhbot2017'
baseurl = 'http://wikipast.epfl.ch/wikipast/'
summary = 'Wikipastbot update'

# Login request
payload={'action':'query','format':'json','utf8':'','meta':'tokens','type':'login'}
r1=requests.post(baseurl + 'api.php', data=payload)

# Login confirm
login_token=r1.json()['query']['tokens']['logintoken']
payload={'action':'login','format':'json','utf8':'','lgname':user,'lgpassword':passw,'lgtoken':login_token}
r2=requests.post(baseurl + 'api.php', data=payload, cookies=r1.cookies)

# Get edit token2
params3='?format=json&action=query&meta=tokens&continue='
r3=requests.get(baseurl + 'api.php' + params3, cookies=r2.cookies)
edit_token=r3.json()['query']['tokens']['csrftoken']

edit_cookie=r2.cookies.copy()
edit_cookie.update(r3.cookies)

# Regexp for definet the type of datum with which we work
content_reexp = re.compile("^\s?(\d{4}\.?\d{0,2}\.?\d{0,2})(\s?\/\s?)?(([\w\s\-,]+))?\.?\s?(.+)$") # Pattern for data
reference_reexp = re.compile("\s*\[\d+\]\s*") # Pattern for reference
date_reexp = re.compile("^\d{4}\.?\d{0,2}\.?\d{0,2}") # Pattern to date

# Function to get list of personages 
def get_all_personages():
    response = urlopen("http://wikipast.epfl.ch/wikipast/index.php/Biographies")
    page_source = response.read()
    soup = BeautifulSoup(page_source,'html.parser')
    result = []
    for primitive in soup.findAll("body"):
        for tableTag in primitive.findAll("table"):
             for trTag in tableTag.findAll("tr"):
                for tdTag in trTag.findAll("td"):
                    for aTag in tdTag.findAll("a"):
                        if aTag.string != None:
                            result.append(aTag.string)

    for idx, _ in enumerate(result): # idx & _ are name of our values in table of results (1:Andre Breton)
        result[idx] = str(result[idx].replace(' ', '_')) # Reformatting the data to get correct link to get access to Biographie
    return result

# Function for verification of personage data
def is_valid_personage_line(datum): # Introduce initial values
    date = datum[0].strip()
    city = None
    info = None

    # Define the type of datum by their length
    if len(datum) == 3: # We have date, city and info 
        city = datum[1].strip()
        info = datum[2].strip()
    elif len(datum) == 2: # We have date and info
        info = datum[1].strip()
    else:
        return False

    # Validate city & Validate date
    if not date_reexp.match(date): # Date don't pass the verification of pattern
        return False
    if city:
        places = GeoText(city) 
        if len(places.cities) == 0:
            if city.count(' ') > 2 or '1' in city: # Check if in place of city we add date or not correct name of city
                return False
    
    return True

# Function for generate summarizing of Biographie
def generate_text_for_personage(name, data):
    result = ''
    last_city = None

    for datum in data: # Checking all datum in list of data
        date = datum[0] 

        # Remove last dot from date
        if date[-1] == '.': date = date[:-1]

        city = datum[1].strip() if len(datum) == 3 else None
        info = datum[-1]

        # Remove incorrect information 
        if len(info) < 5: continue
        
        # Remove last dot from datum-info (sentence)
        if info[-1] == '.': info = info[:-1]
        
        # Add some prefix to date, to make text more readably
        date_prefix = 'En ' if len(date) == 4 else 'Le '
        # Removing city in case if city repat for two consecutive datum or put prefix
        need_city = city != last_city and city != '-'

        city_postfix = ((' a ' + city) if city and need_city else '') + '. '

        result += date_prefix + date + ' ' + info + city_postfix # Final info, more readable sentence 

        last_city = city
    return result

personages = get_all_personages() # To get all personages for address to functioon "get_all_personnages"

data = {} # List of key:value for all data find
correct_data_ratio = {} # List of key:value which keep ratio of lossing info for each personnage 

# Inital values 
total_number_of_content_lines = 0
total_number_of_correct_lines = 0

# Collecting all data from pages of Biograhpie
for personage in personages:
    site = ("http://wikipast.epfl.ch/wikipast/index.php/" + quote(personage))
    response = urlopen(site) 
    page_source = response.read()
    soup = BeautifulSoup(page_source,'html.parser')

    content_div = soup.find(id="mw-content-text") # Defined the principle block of Biographie for each personage
    content_lines = content_div.findAll("li") # Defined line of datum

    data[personage] = [] # Relate data to personage
    total_number_of_content_lines += len(content_lines) # Counting the total number of lines which we find
    
    for content_line in content_lines: # Treatment each line in data we extrect for each personnage
        content_text = content_line.get_text() # From datum extract text
        content_match = content_reexp.match(content_text) # Checking our extracting text with pattern of text

        if content_match: # If checking succeed
            findings = content_reexp.findall(content_text)
            findings_as_array = [x for xs in findings for x in xs] # Made the array of content and personage
            findings_no_duplicates = list(OrderedDict.fromkeys(findings_as_array)) 
            findings_no_duplicates.pop(1) # Removing duplicated datum

            # Check if last is a reference
            if reference_reexp.match(findings_no_duplicates[-1]): findings_no_duplicates.pop(-1)
        
            if is_valid_personage_line(findings_no_duplicates): data[personage].append(findings_no_duplicates)
        
    if len(content_lines) != 0: correct_data_ratio[personage] = len(data[personage]) / len(content_lines) # The ratio of correct data for personnage
    else: correct_data_ratio[personage] = 1 # It's mean that we read zero lines in Biographie of personnage

    total_number_of_correct_lines += len(data[personage]) # Counting total number of lines which we can process to use in summarizing

    print(generate_text_for_personage(personage, data[personage])) # Checking of text which be used for summarizing


correct_data_ratio_overall = total_number_of_correct_lines / total_number_of_content_lines # Checking percentage of data losing after all iteration
print(correct_data_ratio_overall)


Créateurs

Crée par Daniil Morzhakov (dmorzhakov) & Andrey Piskunov (Andrey P).

_/﹋\_
(҂`_´)
<,︻╦╤─ ҉ - - - - - - —
_/﹋\_