[PYTHON] [Version 2020] Grattage et traitement du texte d'Aozora Bunko

Aperçu

Le texte du travail publié dans Aozora Bunko a été gratté avec Python et transformé en une agréable sensation. À l'époque, j'y étais accro ici et là, donc ce mémorandum.

environnement

Acquisition de texte

Tout d'abord, récupérez le texte de l'œuvre auprès d'Aozora Bunko. Ce qu'il faut faire est essentiellement comme décrit dans cet article (https://qiita.com/icy_mountain/items/011c9f56151b9832b54d), [API Aozora Bunko (https://qiita.com/ksato9700/items/626cc82c007ba8337034) Appuyez sur pour récupérer le HTML du corps. À ce moment, la partie de l'ID de travail de l'URL sera définie sur book_id et une variable. Cependant, je ne pouvais pas faire cela dans mon propre environnement. Tout d'abord, je travaillais sur Jupyter Notebook plutôt que sur le terminal, donc je ne peux pas utiliser la commande ! Wget. Donc, si vous recherchez la commande qui envoie une requête GET à l'API en Python, vous verrez ʻurllib2.urlopen () ʻandreqests.req (), mais parmi ceux-ci, ʻurllib2 est une bibliothèque pour Python2. ne peut pas utiliser. Il semble qu'il ait été renommé en ʻurllib3 ou ʻurlliben Python3, mais je n'étais pas sûr, j'ai donc décidé d'utiliser la bibliothèquerequests`. Ainsi, la récupération de l'API par la méthode GET ressemble à ceci:

import requests
res = requests.get('http://pubserver2.herokuapp.com/api/v0.1/books/{book_id}/content?format=html'.format(book_id))

Extraction de texte

Ensuite, convertissez les données HTML récupérées dans un format qui peut être utilisé par BeautifulSoup4.

from bs4 import BeautifulSoup
soup = BeautifulSoup(res.text, 'html.parser')

Pour obtenir le corps de la balise title à partir d'ici, écrivez:

soup.find('title').get_text()
# ->Hisaku Yumeno Narcisse Bleu, Narcisse Rouge

Les titres sont joliment séparés par des espaces demi-largeur, donc si vous faites split () ici, vous pouvez les diviser en nom et titre de l'auteur. Dans certains cas, comme pour les auteurs étrangers, les choses peuvent ne pas bien se passer, de sorte que la présence ou l'absence des informations restantes est également définie dans la variable.

title_list = title.split(' ')
        book_author = title_list[0] #Nom de l'auteur
        book_title = title_list[1] #Titre
        book_title_info = len(title_list) > 2 #Le titre est-il cassé?

D'un autre côté, le corps (dans le vrai sens du mot) est la balise div de la classe main_text, donc cela ressemblerait à ceci:

soup.find('div', {'class': 'main_text'}).get_text()
# -> \n\n\n\r\n\U3000 Utako a été enseigné par un ami, a coupé les racines des jonquilles, mis de la peinture rouge et de la peinture bleue, et les a enterrées dans le coin du jardin.[...]

Cette fois, je veux séparer les phrases par ponctuation, donc je vais créer un format de liste pour chaque phrase comme suit. Si vous voulez la première phrase, vous pouvez l'obtenir comme 0e élément.

text_list = soup.find('div', {'class': 'main_text'}).get_text().split('。')
text_first = text_list[0] + "。" #La première phrase

Purification du texte

Si cela reste tel quel, le texte sera sale, nous allons donc supprimer les éléments inutiles et affiner le texte. Tout d'abord, le code correspondant au code de saut de ligne \ n et l'espace demi-largeur \ u3000 sont mélangés, alors déposez-le avecstrip ()aprèsget_text ().

text_list = soup.find('div', {'class': 'main_text'}).get_text().strip('\r''\n''\u3000').split('。')

Afin de supprimer la partie du rubis entre parenthèses dans le texte, ajoutez le code suivant immédiatement après la conversion pour supprimer le rubis.

    for tag in soup.find_all(["rt", "rp"]):
        tag.decompose() #Supprimer les balises et leur contenu

Parfois, il n'est pas séparé par la ponctuation et la première phrase peut se poursuivre indéfiniment, donc si elle est trop longue, je la remplacerai par une autre chaîne de caractères. La longueur standard 100 est appropriée.

text_first = text_list[0] + "。" if (len(text_list[0]) < 100) else "too long" #début

Enfin, lors de la conversion du processus ci-dessus en une fonction, si l'ID de travail correspondant n'existe pas et que la récupération échoue, une erreur NoneType se produira au moment de l'extraction, excluez donc cela par la présence ou l'absence de balises et de classes. (Je pense qu'il y a une meilleure façon de l'écrire).

if (soup.find('title') is None) or (soup.find('div') is None) or (soup.find('div', {'class': 'main_text'}) is None):
        return [book_id, '', '', '', '', '' ]
else:
        title =  soup.find('title').get_text()
        [...]

bookInfo.py


def bookInfo(book_id):
    import requests
    from bs4 import BeautifulSoup

    res = requests.get(f'http://pubserver2.herokuapp.com/api/v0.1/books/{book_id}/content?format=html')
    
    soup = BeautifulSoup(res.text, 'html.parser')
    for tag in soup.find_all(["rt", "rp"]):
        tag.decompose() #Supprimer les balises et leur contenu
    
    if (soup.find('title') is None) or (soup.find('div') is None) or (soup.find('div', {'class': 'main_text'}) is None):
        return [book_id, '', '', '', '']
    else:
        title =  soup.find('title').get_text()
        title_list = title.split(' ')
        book_author = title_list[0] #Nom de l'auteur
        book_title = title_list[1] #Titre
        book_title_info = len(title_list) > 2 #Le titre est-il cassé?
        
        print(soup.find('div', {'class': 'main_text'}))
        text_list = soup.find('div', {'class': 'main_text'}).get_text().strip('\r''\n''\u3000').split('。')
        text_first = text_list[0] + "。" if (len(text_list[0]) < 100) else "too long" #début
        else:
            text_last = ""
    
        list = [book_id, book_author, book_title, book_title_info, text_first]
        print(list)
        return list
bookInfo(930)
# -> [930,
# 'Hisaku Yumeno',
# 'Le miracle d'Oshie',
# False,
# 'Quand je vois le trou de sommeil de l'infirmière, je dirige un pauvre personnage féminin, donc je pense que c'est difficile à lire et à comprendre, mais je vous pardonnerai plusieurs fois car je me dépêche. S'il vous plaît.']

Sortie CSV de données raffinées

Je voudrais ajouter les listes obtenues en utilisant la fonction ci-dessus afin de créer une liste bidimensionnelle et de la sortir sous forme de fichier CSV. Importez d'abord csv et ouvrez le fichier. À propos, si le fichier existe déjà et que vous souhaitez l'ajouter après le texte intégral au lieu de l'écraser, remplacez w '' par a ''.

import csv
f = open('output.csv', 'w')
writer = csv.writer(f, lineterminator='\n')

Créez une liste vide et tournez la boucle for pour ajouter la valeur de retour de l'exécution de la fonction à la liste. À propos, avec l'API Aozora Bunko, il a fallu quelques secondes pour obtenir un travail. Je pense que si vous demandez trop de requêtes, cela deviendra inutilisable, donc je pense qu'il vaut mieux l'exécuter par petites unités telles que 10 ou 100.

csvlist = []
for i in range(930, 940):
    csvlist.append(bookInfo(i))

Fermez enfin le fichier et vous avez terminé.

writer.writerows(csvlist)
f.close()

Un fichier CSV a été généré. Il est pratique de le charger dans une feuille de calcul Google.

930,Hisaku Yumeno,Le miracle d'Oshie,False,Quand je vois le trou de sommeil de l'infirmière, je dirige un pauvre personnage féminin, donc je pense que c'est difficile à lire et à comprendre, mais je vous pardonnerai plusieurs fois car je me dépêche. S'il vous plaît.

De côté

Recommended Posts

[Version 2020] Grattage et traitement du texte d'Aozora Bunko
Clustering des livres d'Aozora Bunko avec Doc2Vec
Ajouter des lignes et du texte sur l'image
Remarque DJango: depuis le début (traitement de formulaire)
Obtenez l'adresse à partir de la latitude et de la longitude
Récupérez uniquement le texte du formulaire Django.
Gratter l'holojour et l'afficher dans la CLI
Télécharger des images à partir d'un fichier texte contenant l'URL
Macports easy_install résout et exécute automatiquement la version
Passez un tableau de PHP à PYTHON et effectuez un traitement numpy pour obtenir le résultat