« SummarizingBot » : différence entre les versions

De Wikipast
Aller à la navigation Aller à la recherche
Aucun résumé des modifications
Aucun résumé des modifications
Ligne 121 : Ligne 121 :


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

Version du 8 mai 2018 à 11:25

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='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

Cree par Daniil Morzhakov (dmorzhakov) & Andrey Piskunov (Andrey P).

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