[PYTHON] Visualisons le fil 2ch avec WordCloud-analyse morphologique / édition WordCloud-

introduction

2ch prend du temps pour lire chaque threadless, donc J'ai essayé de visualiser les informations du fil avec WordCloud et d'obtenir facilement une image complète. Dans la précédente Scraping Edition, le moins de contenu du groupe de threads souhaité était extrait. Cette fois, dans la deuxième partie, nous analyserons le contenu des leçons collectées la dernière fois et les publierons dans WordCloud.

Flux global

  1. Scraping "vitesse du journal" pour extraire l'URL du thread cible
  2. Racler le fil 2ch pour en extraire moins
  3. [Analyse morphologique du moindre contenu extrait avec Mecab](## Analyse morphologique avec Mecab) ← Explication cette fois
  4. [Sortie avec WordCloud](## Sortie avec WordCloud) ← Explication cette fois

environnement

Comme auparavant, utilisez Google Colaboratory. Google Colaboratory est un environnement d'exécution Python qui s'exécute sur un navigateur. Toute personne disposant d'un compte Google peut l'utiliser. Mecab nécessite une installation supplémentaire (décrite plus loin), mais WordCloud est inclus dans Google Colaboratory par défaut, l'installation n'est donc pas requise.

Code complet

Cliquez pour afficher le texte intégral (y compris le scraping)
#Importer la bibliothèque
import requests, bs4
import re
import time
import pandas as pd
from urllib.parse import urljoin

#Installer les polices localement dans Colab
from google.colab import drive
drive.mount("/content/gdrive")
#Créez à l'avance un dossier appelé police en haut de Mon Drive sur votre Google Drive et placez-y le fichier de police souhaité.
#Copiez chaque dossier localement dans Colab
!cp -a "gdrive/My Drive/font/" "/usr/share/fonts/"

# ------------------------------------------------------------------------
#Préparation
log_database = []  #Une liste qui stocke les informations de fil
base_url = "https://www.logsoku.com/search?q=FFRK&p="

#Mise en place du web scraping
for i in range(1,4):  #À quelle page revenir (ici, provisoirement jusqu'à la 4e page)
  logs_url = base_url+str(i)

  #Corps de traitement de grattage
  res = requests.get(logs_url)
  soup = bs4.BeautifulSoup(res.text, "html.parser")

  #Que faire lorsqu'aucun résultat de recherche n'est trouvé
  if soup.find(class_="search_not_found"):break

  #Récupère la table / ligne où les informations sur les threads sont stockées
  thread_table = soup.find(id="search_result_threads")
  thread_rows = thread_table.find_all("tr")

  #Traitement pour chaque ligne
  for thread_row in thread_rows:
    tmp_dict = {}
    tags = thread_row.find_all(class_=["thread","date","length"])

    #Organisez le contenu
    for tag in tags:
      if "thread" in str(tag):
        tmp_dict["title"] = tag.get("title")
        tmp_dict["link"] = tag.get("href")
      elif "date" in str(tag):
        tmp_dict["date"] = tag.text
      elif "length" in str(tag):
        tmp_dict["length"] = tag.text

    #Seuls ceux qui ont plus de 50 leçons seront ajoutés à la base de données
    if tmp_dict["length"].isdecimal() and int(tmp_dict["length"]) > 50:
      log_database.append(tmp_dict)

  time.sleep(1)

#Convertir en DataFrame
thread_df = pd.DataFrame(log_database)

# ------------------------------------------------------------------------
#Obtenez moins des journaux passés
log_url_base = "http://nozomi.2ch.sc/test/read.cgi/"
res_database = []

for thread in log_database:
  #Nom du tableau et numéro du tableau d'affichage de la liste des journaux précédents.Et générer l'URL du journal passé
  board_and_code_match = re.search("[a-zA-Z0-9_]*?/[0-9]*?/$",thread["link"])
  board_and_code = board_and_code_match.group()
  thread_url = urljoin(log_url_base, board_and_code)

  #Extraire le code HTML de la page de journal précédente
  res = requests.get(thread_url)
  soup = bs4.BeautifulSoup(res.text, "html5lib")

  tmp_dict = {}
  #Informations telles que la date dans la balise dt
  #Le commentaire est stocké dans la balise dd
  dddt = soup.find_all(["dd","dt"])

  for tag in dddt[::-1]:  #Extrait par derrière

    #Extraire uniquement la date de la balise dt
    if "<dt>" in str(tag):
      date_result = re.search(r"\d*/\d*/\d*",tag.text)  #  "(←'"'Pour éviter les anomalies d'affichage de qiita)
      if date_result:
        date_str = date_result.group()
        tmp_dict["date"] = date_str

    #Extraire moins de contenu de la balise dd
    if "<dd>" in str(tag):
      tmp_dict["comment"] = re.sub("\n","",tag.text)

    # tmp_Le contenu stocké dans dict est res_Publier dans la base de données
    if "date" in tmp_dict and "comment" in tmp_dict:
      tmp_dict["thread_title"] = thread["title"]
      res_database.append(tmp_dict)
      tmp_dict = {}

  time.sleep(1)  #promettre

#Convertir en DataFrame
res_df = pd.DataFrame(res_database)

# ------------------------------------------------------------------------

#Bibliothèque d'analyse morphologique MeCab et dictionnaire(mecab-ipadic-NEologd)Installation de
!apt-get -q -y install sudo file mecab libmecab-dev mecab-ipadic-utf8 git curl python-mecab > /dev/null
!git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git > /dev/null 
!echo yes | mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd -n > /dev/null 2>&1
!pip install mecab-python3 > /dev/null

#Éviter les erreurs avec les liens symboliques
!ln -s /etc/mecabrc /usr/local/etc/mecabrc


#Les n(=10000)Séparer par moins et combiner par des virgules
#Le but du délimiteur est que le dernier mécab ne peut pas gérer trop de caractères.
sentences_sep = []
n = 10000
for i in range(0, len(res_df["comment"]), n):
  sentences_sep.append(",".join(res_df["comment"][i: i + n]))

# ------------------------------------------------------------------------
import MeCab

# mecab-ipadic-Spécifiez le chemin où le dictionnaire neologd est stocké
path = "-d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd"
#Au-dessus du chemin (/usr/~) Peut être obtenu avec la commande suivante
# !echo `mecab-config --dicdir`"/mecab-ipadic-neologd"

#Créer un objet Tagger
mecab = MeCab.Tagger(path)

#Effectuer une analyse morphologique pour chaque groupe séparé
chasen_list = [mecab.parse(sentence) for sentence in sentences_sep]

word_list = []

# chasen_Décomposer la liste en une seule ligne
# ex.Géant de fer,Nomenclature propriétaire,Général,*,*,*,Géant de fer,Tetsukyojin,Tetsukyojin)
for chasen in chasen_list:
  for line in chasen.splitlines():
    
    if len(line) <= 1: break

    speech = line.split()[-1]
    if "nom" in speech:
      if  (not "Non indépendant" in speech) and (not "Synonyme" in speech) and (not "nombre" in speech):
        word_list.append(line.split()[0])

word_line = ",".join(word_list)

# ------------------------------------------------------------------------
from wordcloud import WordCloud
import matplotlib.pyplot as plt

f_path = "BIZ-UDGothicB.ttc"  #Doit être copié dans le dossier local des polices de Colab
stop_words = ["https","imgur","net","jpg","com","alors"]

#Génération d'instance (paramétrage)
wordcloud = WordCloud(
    font_path=f_path, #Spécifiez la police
    width=1024, height=640,   #Spécifier la taille de l'image générée
    background_color="white",   #Spécifier la couleur d'arrière-plan
    stopwords=set(stop_words),   #Mots qui ne sont pas affichés intentionnellement
    max_words=350,   #Nombre maximum de mots
    max_font_size=200, min_font_size=5,   #Plage de tailles de police
    collocations = False    #Affichage des mots composés
    )

#Génération d'images
output_img = wordcloud.generate(word_line)

#indiquer
plt.figure(figsize=(18,15))  #Spécifiez la taille à afficher avec figsize
plt.imshow(output_img)
plt.axis("off")  #Cacher l'échelle
plt.show()

La description

Analyse morphologique par Mecab

L'analyse morphologique est le processus de décomposition d'une phrase en langage naturel en mots (plus précisément, des unités appelées éléments morphologiques qui sont plus fins que des mots). Contrairement à l'anglais, le japonais n'insère pas d'espaces entre les mots, il est donc nécessaire d'effectuer ** une analyse morphologique pour séparer les mots **. Il existe plusieurs outils d'analyse morphologique, mais cette fois nous utiliserons "Mecab", qui a une vitesse de traitement élevée et une grande précision.

Installez Mecab

Mecab n'est pas inclus dans Google Colaboratory par défaut, alors installez-le en exécutant ce qui suit à chaque fois.

#Installez Mecab
!apt-get -q -y install sudo file mecab libmecab-dev mecab-ipadic-utf8 git curl python-mecab > /dev/null
!pip install mecab-python3 > /dev/null

#Éviter les erreurs avec les liens symboliques
!ln -s /etc/mecabrc /usr/local/etc/mecabrc

Spécifier un dictionnaire (mecab-ipadic-NEologd)

Le dictionnaire par défaut de Mecab "mecab-ipadic" n'est pas très précis pour les nouveaux mots. Par conséquent, nous recommandons la spécification du dictionnaire ** "mecab-ipadic-NEologd" **. "Mecab-ipadic-NEologd" est l'un des dictionnaires système qui peuvent être utilisés avec Mecab, et comme il est fréquemment mis à jour, il se caractérise par sa résistance aux nouveaux mots **. Par exemple, lorsque le mot-clé "Aeris" est analysé morphologiquement. Dans le dictionnaire par défaut, il est divisé en "air / écureuil", mais dans "mecab-ipadic-NEologd", "airis" est correctement jugé comme un mot. Dans un environnement où les nouveaux mots tels que 2ch sont bâclés, l'utilisation de "mecab-ipadic-NEologd" devrait améliorer la précision de l'analyse. La méthode d'installation est la suivante.

#dictionnaire(mecab-ipadic-NEologd)Installation de
!git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git > /dev/null 
!echo yes | mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd -n > /dev/null 2>&1

Lors d'un appel ultérieur avec Mecab, il est nécessaire de spécifier le chemin où le dictionnaire mecab-ipadic-neologd est stocké, définissez-le donc.

path = "-d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd"
Si cela ne fonctionne pas (cliquez pour afficher)
Le chemin ci-dessus (/ usr / ~) devrait être fondamentalement correct, mais s'il ne fonctionne pas, obtenez le chemin du dictionnaire avec la commande suivante.
!echo `mecab-config --dicdir`"/mecab-ipadic-neologd"

Prétraitement des données pour l'analyse morphologique

Il y a deux points lors de la transmission de données texte à Mecab -Mecab transmet une donnée de type str (dans ce cas, une chaîne de caractères combinée avec une virgule) -Si le type de chaîne ci-dessus contient trop de données ** , l'analyse échouera, il est donc nécessaire de diviser et de transmettre les données à Mecab **. Par conséquent, le contenu moins gratté précédemment (type DataFrame: res_df) est connecté à une chaîne pour 10000 moins. Je l'ai stocké dans la liste l'un après l'autre.

#Les n(=10000)Séparer par moins et combiner par des virgules
#Le but du délimiteur est que le dernier mécab ne peut pas gérer trop de caractères.
sentences_sep = []
n = 10000
for i in range(0, len(res_df["comment"]), n):
  sentences_sep.append(",".join(res_df["comment"][i: i + n]))

Effectuer une analyse morphologique

Mecab est exécuté dans le flux de (1) Création de l'instance Mecab.Tagger(2) Passer le texte cible à l'instance et analyser (analyser). Lors de la création de l'instance de ①, spécifiez l'option d'analyse. Je souhaite utiliser le dictionnaire système "mecab-ipadic-NEologd" mentionné ci-dessus, spécifiez donc le chemin obtenu à l'avance. Le résultat de l'analyse de ② est acquis par Tagger instance.parse (str type). Dans ce cas, comme mentionné ci-dessus, le moins de groupe était divisé en un type de liste, j'ai donc essayé de le traiter en utilisant la notation d'inclusion comme python.

import MeCab

#Génération d'instance
mecab = MeCab.Tagger(path)

#Effectuer une analyse morphologique pour chaque groupe séparé
chasen_list = [mecab.parse(sentence) for sentence in sentences_sep]

La sortie sera de type str avec les sauts de ligne et les délimiteurs de tabulation suivants.

Suppression du bruit

Parmi les mots découpés, les mots qui n'ont pas de sens en eux-mêmes, tels que «mots auxiliaires», «verbes auxiliaires» et «adjoints», peuvent être du bruit, ils sont donc exclus. Cette fois, j'ai essayé d'extraire simplement les "noms". Cependant, parmi la nomenclature, la non-indépendance, les synonymes, les nombres, etc. sont exclus car ils sont susceptibles de provoquer du bruit. En tant que processus, le type str du résultat de sortie Mecab est décomposé ligne par ligne avec .splitlines () → ensuite décomposé en informations de mots et de parties de paroles avec .split () → Lorsque les informations de mot de partie correspondent à la condition, la partie de mot est ajoutée à word_list.

#Suppression du bruit (mots de partie inutiles)
for chasen in chasen_list:
  for line in chasen.splitlines():
    
    if len(line) <= 1: break

    speech = line.split()[-1]  ##Extraire les informations des paroles de la partie
    if "nom" in speech:
      if  (not "Non indépendant" in speech) and (not "Synonyme" in speech) and (not "nombre" in speech):
        word_list.append(line.split()[0])

ʻSi len (line) <= 1: break` sur le chemin est une contre-mesure d'erreur (probablement due à EOS). Enfin, le type de liste est concaténé en un type str.

#Concaténation de mots
word_line = ",".join(word_list)

Sortie avec WordCloud

(Préparation) Installation des polices japonaises

Lors de l'application du japonais à WordCloud, il est nécessaire de spécifier la police pour être compatible avec le japonais. S'il est local, il suffit de spécifier le chemin de la police souhaitée, Dans le cas de Google Colaboratory, c'est un peu gênant.    ↓ Tout d'abord, ** copiez à l'avance le fichier de police souhaité sur votre Google Drive ** (seules les polices TrueType sont prises en charge). L'emplacement est arbitraire, mais par analogie avec l'article auquel j'ai fait référence, j'ai créé un dossier «font» dans My Drive Top et y ai stocké les fichiers. Montez Google Drive sur le colaboratoire.

#Installer les polices localement dans Colab
from google.colab import drive
drive.mount("/content/gdrive")

Lorsque vous exécutez ce qui précède, un lien pour monter Google Drive s'affiche. Cliquez dessus pour sélectionner un compte → Autoriser → Entrez le code affiché sur Google Colaboratory pour le monter.

Utilisez la commande pour copier le fichier de police avec le dossier dans le dossier spécifié localement dans Colaboratory.

!cp -a "gdrive/My Drive/font/" "/usr/share/fonts/"
Si cela ne fonctionne pas (cliquez pour afficher) Une fois, pour une raison quelconque, j'ai eu une erreur lors du montage du lecteur et je n'ai pas pu installer la police. Dans ce cas, téléchargez le fichier directement dans le dossier de polices local de Google Colaboratory. ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642673/74d79528-0f78-b5ce-8275-079f4e74f2ce.png)

Exécuter Word Cloud

Importez la bibliothèque WordCloud et créez une instance avec WordCloud (). Divers paramètres de sortie peuvent être définis en donnant un argument dans this ().

from wordcloud import WordCloud

f_path = "BIZ-UDGothicB.ttc"  #Doit être copié dans le dossier local des polices de Colab
stop_words = ["https","imgur","net","jpg","com","alors"]

#Génération d'instance (paramétrage)
wordcloud = WordCloud(
    font_path=f_path, #Spécifiez la police
    width=1024, height=640,   #Spécifier la taille de l'image générée
    background_color="white",   #Spécifier la couleur d'arrière-plan
    stopwords=set(stop_words),   #Mots qui ne sont pas affichés intentionnellement
    max_words=350,   #Nombre maximum de mots
    max_font_size=200, min_font_size=5,   #Plage de tailles de police
    collocations = False    #Affichage des mots composés
    )

Le contenu de chaque paramètre est le suivant.

Paramètres La description Définir la valeur
font_path Spécifiez la police Le chemin de police mentionné ci-dessus (f_path)
colormap Jeu de couleurs de police
(Spécifié par la carte de couleurs matplotlib)
Non défini (par défaut: viridis)
width Largeur de l'image générée 1024
height Hauteur de l'image générée 640
background_color Couleur de l'arrière plan white
stopwords Mots qui ne sont pas affichés intentionnellement (définis) ["https","imgur","net","jpg","com","alors"]
max_words Nombre maximum de mots à afficher 350
max_font_size Taille de police pour le plus de mots 200
min_font_size La plus petite taille de police de mots 5
collocations Afficher ou non les mots connectés False

Pour les paramètres autres que ceux ci-dessus, reportez-vous à l'article de l'article ci-dessous. Générez une figure à partir de la chaîne de caractères cible avec la méthode .generate (concatenated word group: str type) de l'instance wordcloud générée ci-dessus.

#Générer une image WordCloud en donnant une chaîne
output_img = wordcloud.generate(word_line)

Affiché avec matplotlib

import matplotlib.pyplot as plt

plt.figure(figsize=(18,15))  #Spécifiez la taille à afficher avec figsize
plt.imshow(output_img)
plt.axis("off")  #Cacher l'échelle
plt.show()
J'ai pu l'afficher en toute sécurité.

Impressions / avenir

Pour le moment, j'ai réussi à le visualiser, mais j'ai l'impression que c'est devenu flou. Je pense qu'une des raisons est que «l'axe du temps» et la «corrélation entre les mots» ont disparu. Alors quand tu as le temps ・ Affichage de la corrélation avec l'axe du temps (graphique) ・ Je veux jouer avec le réseau de cooccurrence. ~~ Je suis fatigué d'écrire un long article ~~ Je ne sais pas s'il faut écrire un article.

Article de référence

Résumé de l'utilisation de Google Colab Installez MeCab et ipadic-NEologd sur Google Colab Comment mettre votre police préférée dans Google Colaboratory et l'utiliser avec matplotlib J'ai essayé de créer Word Cloud avec Python ← Il y a une explication des paramètres wordcloud non mentionnés cette fois

Recommended Posts