SummarizingBot
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
Code du SummarizingBot
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).
_/﹋\_
(҂`_´)
<,︻╦╤─ ҉ - - - - - - —
_/﹋\_