[PYTHON] Lassen Sie uns 2ch Thread mit WordCloud-Scraping- visualisieren

Einführung

2ch ist ein bekanntes anonymes Bulletin Board und bietet eine Vielzahl von Informationen. Wenn Sie jedoch jeden Thread weniger lesen, dauert es enorm lange, bis das gesamte Bild angezeigt wird. Daher habe ich versucht, diese Informationen mit WordCloud zu visualisieren und auf einfache Weise das gesamte Bild zu erhalten.

  • Das Obige ist das Ergebnis der Thread-Suche mit "FFRK". Dies ist die Ausgabe der Lektionen der letzten 8 Monate in WordCloud. * *
  • Es ist ungefähr ein Jahr her, seit das Synchro-Mysterium implementiert wurde, aber es ist zu sehen, dass über das Awakening-Mysterium noch mehr gesprochen wird. Neben Deshi Urara von FFRK Ori-chara wird relativ viel über Butts, Edge, Cloud, Mog usw. gesprochen. Kann erwartet werden. * *

Ich bin ein Anfänger sowohl im Scraping als auch in der Verarbeitung natürlicher Sprache, aber ich würde gerne darüber schreiben, weil es auf meine eigene Weise Gestalt angenommen hat. Dieses Mal werden wir als ersten Teil durch Web-Scraping Thread-Informationen zu weniger Inhalten sammeln.

Gesamtdurchfluss

  1. [Scraping "log speed", um die URL des Ziel-Threads zu extrahieren](#### [Scraping der Thread-Liste von "log speed"]) ← Erklärung dieses Mal
  2. [2-Kanal-Thread abkratzen, um weniger zu extrahieren](#### [2-Kanal-Thread abkratzen]) ← Erklärung dieses Mal
  3. Morphologische Analyse des mit Mecab extrahierten weniger Inhalts
  4. Ausgabe mit WordCloud

Vollständiger Code

Klicken Sie hier, um den vollständigen Text anzuzeigen (einschließlich anderer Verarbeitung als Scraping).
#Bibliothek importieren
import requests, bs4
import re
import time
import pandas as pd
from urllib.parse import urljoin

#Installieren Sie Schriftarten lokal in Colab
from google.colab import drive
drive.mount("/content/gdrive")
#Erstellen Sie vorab einen Ordner mit dem Namen "Schriftart" oben auf "Mein Laufwerk" auf Ihrem Google Drive und legen Sie die gewünschte Schriftartdatei darin ab.
#Kopieren Sie jeden Ordner lokal nach Colab
!cp -a "gdrive/My Drive/font/" "/usr/share/fonts/"

# ------------------------------------------------------------------------
#Vorbereitung
log_database = []  #Eine Liste, in der Thread-Informationen gespeichert sind
base_url = "https://www.logsoku.com/search?q=FFRK&p="

#Implementierung von Web Scraping
for i in range(1,4):  #Auf welche Seite Sie zurückkehren sollten (hier vorläufig bis zur 4. Seite)
  logs_url = base_url+str(i)

  #Schrottverarbeitungskörper
  res = requests.get(logs_url)
  soup = bs4.BeautifulSoup(res.text, "html.parser")

  #Was tun, wenn keine Suchergebnisse gefunden werden?
  if soup.find(class_="search_not_found"):break

  #Ruft eine Tabelle / Zeile ab, in der Thread-Informationen gespeichert sind
  thread_table = soup.find(id="search_result_threads")
  thread_rows = thread_table.find_all("tr")

  #Verarbeitung für jede Zeile
  for thread_row in thread_rows:
    tmp_dict = {}
    tags = thread_row.find_all(class_=["thread","date","length"])

    #Organisieren Sie den Inhalt
    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

    #Nur diejenigen mit mehr als 50 Lektionen werden der Datenbank hinzugefügt
    if tmp_dict["length"].isdecimal() and int(tmp_dict["length"]) > 50:
      log_database.append(tmp_dict)

  time.sleep(1)

#In DataFrame konvertieren
thread_df = pd.DataFrame(log_database)

# ------------------------------------------------------------------------
#Holen Sie weniger aus früheren Protokollen
log_url_base = "http://nozomi.2ch.sc/test/read.cgi/"
res_database = []

for thread in log_database:
  #Boardname und Bulletin Board-Nummer aus der Liste der letzten Protokolle.Und generieren Sie die URL des letzten Protokolls
  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)

  #Extrahieren Sie HTML von der letzten Protokollseite
  res = requests.get(thread_url)
  soup = bs4.BeautifulSoup(res.text, "html5lib")

  tmp_dict = {}
  #Informationen wie Datum im dt-Tag
  #Der Kommentar wird im dd-Tag gespeichert
  dddt = soup.find_all(["dd","dt"])

  for tag in dddt[::-1]:  #Auszug von hinten

    #Extrahieren Sie nur das Datum aus dem dt-Tag
    if "<dt>" in str(tag):
      date_result = re.search(r"\d*/\d*/\d*",tag.text)  #  "(←'"'Es ist mir egal (um Anomalien des Qiita zu vermeiden)
      if date_result:
        date_str = date_result.group()
        tmp_dict["date"] = date_str

    #Extrahieren Sie weniger Inhalt aus dem dd-Tag
    if "<dd>" in str(tag):
      tmp_dict["comment"] = re.sub("\n","",tag.text)

    # tmp_Die in dict gespeicherten Inhalte sind res_In Datenbank posten
    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)  #versprechen

#In DataFrame konvertieren
res_df = pd.DataFrame(res_database)

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

#Morphologische Analysebibliothek MeCab und Wörterbuch(mecab-ipadic-NEologd)Installation von
!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

#Vermeiden Sie Fehler mit symbolischen Links
!ln -s /etc/mecabrc /usr/local/etc/mecabrc

#Wordcloud-Installation
!pip install wordcloud

#Kombinieren Sie umso weniger
sentences = ",".join(res_df["comment"])
sentences_sep = []
n = 10000
for i in range(0,len(sentences), n):
  sentences_sep.append(sentences[i:i + n])

#Les n(=1000)Durch weniger trennen und mit Kommas kombinieren
#Der Zweck des Trennzeichens besteht darin, dass das spätere Mecab nicht zu viele Zeichen verarbeiten kann.
sentences_sep = []
n = 1000
for i in range(0, len(res_df["comment"]), n):
  sentences_sep.append(",".join(res_df["comment"][i: i + n]))

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

# mecab-ipadic-Geben Sie den Pfad an, in dem das neologd-Wörterbuch gespeichert ist
path = "-d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd"
#Über dem Pfad (/usr/~) Kann mit dem folgenden Befehl erhalten werden
# !echo `mecab-config --dicdir`"/mecab-ipadic-neologd"

#Erstellen eines Tagger-Objekts
mecab = MeCab.Tagger(path)

#Führen Sie für jede getrennte Gruppe eine morphologische Analyse durch
chasen_list = [mecab.parse(sentence) for sentence in sentences_sep]

word_list = []

# chasen_Zerlegen Sie die Liste in eine Zeile
# ex.Eisen Riesennomen,Proprietäre Nomenklatur,Allgemeines,*,*,*,Eisenriese,Tetsukyojin,Tetsukyojin)
for chasen in chasen_list:
  for line in chasen.splitlines():
    
    if len(line) <= 1: break

    speech = line.split()[-1]
    if "Substantiv" in speech:
      if  (not "Nicht unabhängig" in speech) and (not "Gleichbedeutend" in speech) and (not "Nummer" in speech):
        word_list.append(line.split()[0])

word_line = ",".join(word_list)

# ------------------------------------------------------------------------
from wordcloud import WordCloud
import matplotlib.pyplot as plt
#Es ist erforderlich, Schriftarten vorab lokal in Colab zu installieren.
f_path = "BIZ-UDGothicB.ttc"
stop_words = ["https","imgur","net","jpg","com","damit"]

wordcloud = WordCloud(
    font_path=f_path,
    width=1024, height=640,   # default width=400, height=200
    background_color="white",   # default=”black”
    stopwords=set(stop_words),
    max_words=350,   # default=200
    max_font_size=200,   #default=4
    min_font_size=5,   #default=4
    collocations = False   #default = True
    ).generate(word_line)
plt.figure(figsize=(18,15))
plt.imshow(wordcloud)
plt.axis("off") #Speicher ausblenden
plt.show()

Umgebung ~ Google Colaboratory ~

** Verwenden Sie Google Colaboratory ** Google Colaboratory ist eine Python-Ausführungsumgebung in einem Browser, die jeder verwenden kann, solange er über ein Google-Konto verfügt. Es wird häufig in Situationen des maschinellen Lernens verwendet, da es eine leistungsstarke GPU verwenden kann. Es wird jedoch auch empfohlen, wenn Sie sich frei fühlen möchten, weil ** Sie keine Bibliothek installieren müssen, wenn Sie nur kratzen **. (Für Mecab, WordCloud usw. ist eine zusätzliche Installation erforderlich, die beim nächsten Mal erläutert wird.) Im folgenden Artikel erfahren Sie, wie Sie Google Colaboratory verwenden ⇒ Zusammenfassung der Verwendung von Google Colab (Ich habe es nicht ausprobiert, aber ich denke, es ist möglich, wenn Sie Colab nicht haben, wenn Sie die Bibliothek usw. installieren.)

Kommentar

Grundlagen des Schabens

Holen Sie sich das Objekt der Webseite mit "request.get ()" und analysieren Sie es mit HTML mit "bs4.BeautifulSoup ()". Der Eingang zum Schaben. "Html.parser" ist eine Parser-Spezifikation. Für die Protokollgeschwindigkeit ist "html.parser" (ein häufig verwendeter) in Ordnung, aber ** 2ch verwendet einen Parser namens "html5lib" </ font> **. (Weil "html.parser" aus irgendeinem Grund nicht analysiert werden kann)

  #Schrottverarbeitungskörper
  res = requests.get(logs_url)
  soup = bs4.BeautifulSoup(res.text, "html.parser")
versprechen

Wenn Sie durch Scraping mehrmals auf eine Website zugreifen, fügen Sie zwischen den Wiederholungen "time.sleep (1)" ein, um den Server nicht zu überlasten.

[Verschrotten der Thread-Liste von "Protokollgeschwindigkeit"]

Suchen Sie nach Threads (einschließlich aktueller Threads), die ein beliebiges Schlüsselwort aus "Protokollgeschwindigkeit" enthalten, und extrahieren Sie die Ergebnisse. Protokollgeschwindigkeit: https://www.logsoku.com/

Holen Sie sich die URL zu kratzen

Als ich versuchte, auf der oben genannten Site zu suchen, stellte sich heraus, dass es sich um eine URL wie diese handelte. Suche nach "FFRK": https://www.logsoku.com/search?q=FFRK 2. und nachfolgende Seiten: https://www.logsoku.com/search?q=FFRK&p=2

Aus der URL der Suchergebnisse haben wir Folgendes gefunden. ・ Die URL von https://www.logsoku.com/search? Mit q = ist die Suchergebnisseite (q ist Quest). q?) ・ Sie können direkt auf jede Seite des Suchergebnisses mit p = zugreifen (p ist p der Seite?) ・ Die erste Seite kann auch mit p = 1 angezeigt werden. ・ Auf Seiten ohne Suchergebnisse kann zugegriffen werden

Aus dem Obigen geht hervor, dass die URL der Seite, auf die zugegriffen werden soll, durch Drehen der for-Anweisung abgerufen werden kann. Glücklicherweise können Sie auf die Seite selbst zugreifen, auch wenn die Suchseite nicht gültig ist. Auf Seiten ohne Suchergebnisse gibt es eine Klasse namens "search_not_found". Verwenden Sie diese Klasse daher zur Beurteilung.

base_url = "https://www.logsoku.com/search?q=FFRK&p="
for i in range(1,100):
  logs_url = base_url+str(i)

  #Scraping durchführen
  res = requests.get(logs_url)
  soup = bs4.BeautifulSoup(res.text, "html.parser")

  #Was tun, wenn keine Suchergebnisse gefunden werden?
  if soup.find(class_="search_not_found"):break
 :
(Verarbeitung für jede Seite)
 :
Scraping für jede Suchseite

Sehen Sie sich vor dem eigentlichen Scraping den HTML-Code der Zielseite an und überlegen Sie, wie Sie ihn verarbeiten sollen. Zu diesem Zeitpunkt sind ** "Entwicklungstools" **, die in jedem Browser bereitgestellt werden, nützlich. Drücken Sie im Browser F12, um es zu starten. Wenn Sie in diesem Zustand auf ** "Element aus Seite auswählen" ** klicken, können Sie sehen, wohin Sie im HTML-Code zeigen, wenn Sie den Cursor an einer beliebigen Stelle auf der Webseite betrachten.

Beim Betrachten des 2-Kanal-Threads mit dem Entwicklungstool habe ich die folgenden Punkte gefunden. -Alle notwendigen Informationen werden unter div # search_result_threads gespeichert. -Informationen für einen Thread werden in jedem tr-Tag gespeichert. -Der Thread-Titel und der Link werden in a.thred im tr-Tag gespeichert. -Die Anzahl der Threads wird in td.length im tr-Tag gespeichert. -Das Aktualisierungsdatum und die Aktualisierungszeit des Threads werden in td.date im tr-Tag gespeichert.

Basierend auf diesen wird das Schaben wie folgt durchgeführt. Threads mit weniger als 50 Threads werden weggelassen, da die Wahrscheinlichkeit einer Duplizierung hoch ist. Es ist leicht zu verstehen, ob das Extraktionsergebnis in einem Wörterbuchtyp gespeichert ist, und es kann leicht in das später beschriebene DataFrame-Format konvertiert werden.

  #Ruft eine Tabelle / Zeile ab, in der Thread-Informationen gespeichert sind
  thread_table = soup.find(id="search_result_threads")
  thread_rows = thread_table.find_all("tr")

  #Verarbeitung für jede Zeile
  for thread_row in thread_rows:
    tmp_dict = {}
    tags = thread_row.find_all(class_=["thread","date","length"])

    #Organisieren Sie den Inhalt
    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

    #Nur diejenigen mit mehr als 50 Lektionen werden der Datenbank hinzugefügt
    if tmp_dict["length"].isdecimal() and int(tmp_dict["length"]) > 50:
      log_database.append(tmp_dict)

  time.sleep(1)

Vorläufig konnte ich die 2ch-Thread-Informationen abrufen, nach denen ich gesucht habe. Konvertieren Sie es in das Pandas DataFrame-Format, damit es später problemlos verarbeitet werden kann.

thread_df = pd.DataFrame(log_database)  #Umwandlung

Anzeige

thread_df
Zuerst konnte ich die Thread-Liste extrahieren.

[2ch Fäden abkratzen]

Basierend auf den oben erfassten Thread-Informationen wird der Inhalt des Threads extrahiert. Geben Sie in 2ch die Thread-URL wie folgt an. "Http://nozomi.2ch.sc/test/read.cgi/" + "Boardcode /" + "Bulletin Board No./ "" Der Board-Code und die Bulletin-Board-Nr. Sind die regulären Ausdrücke re.search (" [a-zA-Z0-9_] *? / [0-9] *? / $ ", Thread [" aus dem Link, der durch das obige Scraping erhalten wurde. Extrahiert mit dem Link "].

#Holen Sie weniger aus früheren Protokollen
log_url_base = "http://nozomi.2ch.sc/test/read.cgi/"
res_database = []

for thread in log_database:
  #Boardname und Bulletin Board-Nummer aus der Liste der letzten Protokolle.Und generieren Sie die URL des letzten Protokolls
  board_and_code_match = re.search("[a-zA-Z0-9_]*?/[0-9]*?/$",thread["link"])
  board_and_code = board_and_code_match.group()  #Konvertieren Sie Ergebnisse von Objekten mit regulären Ausdrücken
  thread_url = urljoin(log_url_base, board_and_code)
 :
(Verarbeitung für jeden Thread)
 :
Erwerb von weniger Inhalten / Veröffentlichungsdatum

Verwenden Sie das Browser-Entwicklungstool (F12) sowie die Protokollgeschwindigkeit, um Trends beim Scraping von Seiten zu untersuchen. Ich konnte diese Tendenz auf 2ch fangen. ・ Eins weniger mit einem Satz dd-Tag und dt-Tag. ・ Es ist unwahrscheinlich, dass die Tags dd und dt an einem anderen Ort als Les verwendet werden. ・ Neben dem Datum und der Uhrzeit der Antwort enthält das dd-Tag auch Informationen wie Bügeleisen und ID. -Der Inhalt der Antwort wird im dt-Tag gespeichert.

Basierend auf diesen wurde das Schaben wie folgt durchgeführt. Wie oben erwähnt ** verwendet der Parser "html5lib" **.

  #Extrahieren Sie HTML von der letzten Protokollseite
  res = requests.get(thread_url)
  soup = bs4.BeautifulSoup(res.text, "html5lib")  #Verwenden Sie html5lib für 2ch

  tmp_dict = {}
  #Informationen wie Datum im dt-Tag
  #Je weniger Inhalt im dd-Tag gespeichert ist
  dddt = soup.find_all(["dd","dt"])

  for tag in dddt[::-1]:  #Auszug von hinten

    #Extrahieren Sie nur das Datum aus dem dt-Tag
    if "<dt>" in str(tag):
      date_result = re.search(r"\d*/\d*/\d*",tag.text)  #  "(←'"'Es ist mir egal (um Anomalien des Qiita zu vermeiden)
      if date_result:
        date_str = date_result.group()
        tmp_dict["date"] = date_str

    #Extrahieren Sie weniger Inhalt aus dem dd-Tag
    if "<dd>" in str(tag):
      tmp_dict["comment"] = re.sub("\n","",tag.text)

    # tmp_Die in dict gespeicherten Inhalte sind res_In Datenbank posten
    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)  #versprechen

Als Fluss haben wir zuerst dd-Tags und dt-Tags durcheinander extrahiert, sie einzeln zerlegt, beurteilt, ob es sich um dt-Tags oder dd-Tags handelt, und sie in einem Wörterbuchtyp gespeichert. (Tatsächlich werden das zusammen extrahierte dd-Tag und das dt-Tag regelmäßig angeordnet, und eine Beurteilung ist nicht erforderlich. Ich habe jedoch versucht, eine Beurteilung vorzunehmen, damit sie behandelt werden kann, wenn ein anderes Muster auftritt.)

In DataFrame konvertieren
res_df = pd.DataFrame(res_database)

Anzeige

res_df

Ich konnte die Informationen zu den 2-Stunden-Lektionen und dem Buchungsdatum sicher extrahieren.

Zukunftspläne

Das nächste Mal, nachdem der extrahierte Inhalt einer morphologischen Analyse unterzogen wurde, wird er in WordCloud ausgegeben.

Recommended Posts

Lassen Sie uns 2ch Thread mit WordCloud-Scraping- visualisieren
Lassen Sie uns 2ch Thread mit WordCloud-morphologische Analyse / WordCloud Edition- visualisieren
Mit Pandas schnell visualisieren
Visualisieren Sie Ansprüche mit AI
Visualisieren Sie 2019 nem mit WordCloud
Visualisieren Sie Standortinformationen mit Basemap
Lassen Sie uns das Wissen über Wikidata mit Neo4j visualisieren
Visualisieren Sie Python-Paketabhängigkeiten mit graphviz
[Tkinter] Steuert Threads mit Ereignisobjekt