« SummarizingBot » : différence entre les versions
Aucun résumé des modifications |
|||
Ligne 111 : | Ligne 111 : | ||
Code du '''SummarizingBot''' | Code du '''SummarizingBot''' | ||
<pre> | <pre> | ||
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 = "Andrey P" | |||
passw = 'H186Ko51' | |||
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) | |||
</pre> | </pre> | ||
Version du 7 mai 2018 à 15:35
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 du page Wiki du chaque personnage (tout le personnages traite se trouvent dans le page Wiki Biographies). Le volume approximatif du résume est de 200 mots. La taille du résume fortement dépend des qualités données d'origine, acquis du page du personnage. C'est-à-dire, leur volume (au moins 15 entrées) et l'exactitude de l'entrée (syntaxes du Wiki doit être respecter).
Le principe clé du bot est d'analyse des données (entrées) en utilisant le concept de Expression Régulière (Regular Expression ou RegExp) pour identifier différentes parties d’entrée (une ligne de donnée). Grâce à cette analyse, on va obtenir des blocs d'information à partir de quelle chaque entrée est consister. Ces blocs sont suivants : Le bloc du Data, Le bloc du Ville et, finalement, Le bloc d’information sur évènement. Ensuite, on tire les informations supplémentaires de chaque bloc avec les traiter avec les fonctions différentes. 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 éliminer ce bloc. On peut facilement applique les fonctions pour chaque cas particulier des donnes dans le bloc pour crée une structure correct applicable en résume.
La performance du code nous avons donné l'environ 93% de données totale est traité est utilisé pour crée 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 du tous les pages de la Biographie.
Expresion 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 quelque l'exemple de 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 le date (pas bon format).
[1993.05.23][Milan11][Information] ou [1993.05.23][Quelque part][Information] ---> "False'', car on a une lieu qui a pas une bon format (chiffres) ou il y a nombreux de espaces dans le série.
Points Critique des Performances
Le point critique sont suivantes :
- Erreurs dans la saisie des données dans la biographie. Non-respect la syntaxe poser pour l'entrée dans le Biographie.
- Le problème de la langue utilisée. 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 assez compliquée. Ce-ceci impose un grand nombre des fonctions pour avoir un texte lisible et grammaticalement correct.
- Étude en détaillée du type de données d'entrée pour améliorer les motifs. Pour obtenir un pourcentage plus faible des données perdues et augmenter la quantité d'informations obtenues à partir d’une entrée.
Exemple
L'exemple base sur le 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 = "Andrey P" passw = 'H186Ko51' 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
Cree par Daniil Morzhakov (dmorzhakov) & Andrey Piskunov (Andrey P).
_/﹋\_
(҂`_´)
<,︻╦╤─ ҉ - - - - - - —
_/﹋\_