2019年内に投稿した記事でワードクラウドを作ってみた
移転前のブログ(Blogger)のバックアップファイルから、Pythonでワードクラウドを作った記録です。
記事の取得
「設定」→「その他」→「コンテンツをバックアップ」から、XMLファイルを取得します。
コーディング
処理の流れはこんな感じです。
- XMLをパースして本文のみ抽出
- HTMLタグを除去
- 形態素解析して品詞の原形のリストにする
- WordCloudパッケージに渡して画像を作成
# -*- coding: utf-8 -*-
import logging
import sys
import os
import xml.etree.ElementTree as ET
from django.utils import html
from janome.tokenizer import Tokenizer
from wordcloud import WordCloud
# =======================================================
DIR_NAME = "C:\\Users\\hogehoge\\デスクトップ\\wc"
XML_FILE: str = 'blog-12-26-2019.xml'
FONT_PATH = "C:\\Windows\\Fonts\\UDDigiKyokashoN-R.ttc"
WC_WIDTH = 300
WC_HEIGHT = 300
OUTPUT_FILE = 'wc2.png'
BACKGROUND = 'darkslategray'
COLOR_MAP = 'summer'
STOP_WORDS = {u'なる', u'いる', u'できる', u'ある', u'ない',
u'こと', u'自分', u'する', u'もの', u'よう',
u'れる', u'られる',
u'Unsplash', u'Photo', u'nbsp', u'by', u'on'}
CODE_TAG_F = "<code" # not typo
CODE_TAG_T = "</code>"
# =======================================================
# ログの設定
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s')
os.chdir(DIR_NAME)
logging.debug('current directory...' + os.getcwd())
# =======================================================
def parseLog():
funcname = sys._getframe().f_code.co_name
logging.debug(funcname + " start")
logging.debug("parse the file: {0}".format(XML_FILE))
logging.debug("file size: {0}bytes".format(os.path.getsize(XML_FILE)))
textWithHtml = ''
count = 0 # デバッグ用
tree = ET.parse(XML_FILE)
root = tree.getroot()
# 本文はentry要素の中に入っている
for entry in root.iter("{http://www.w3.org/2005/Atom}entry"):
entryId = str(entry.find("{http://www.w3.org/2005/Atom}id").text)
# 投稿記事はidが~.post-~になっている。それ以外はブログ設定項目など
# 下書き記事はcontrol要素が記述されているので除外
if entryId.find('.post-') > 0 and entry.find("{http://purl.org/atom/app#}control") is None:
# デバッグ用にタイトル出力
logging.debug(entry.find("{http://www.w3.org/2005/Atom}title").text)
# 本文を全部結合
textWithHtml += ' ' + entry.find("{http://www.w3.org/2005/Atom}content").text
count += 1
logging.debug(funcname + ' end. read ' + str(count) + ' posts.')
return textWithHtml
# =======================================================
def removeHtml(text):
funcname = sys._getframe().f_code.co_name
logging.debug(funcname + ' start')
# logging.debug(text)
text_no_code = ''
# codeタグで囲まれた部分は除く
if CODE_TAG_F in text:
logging.debug('delete code')
text2 = text.split(CODE_TAG_F)
for text3 in text2:
if CODE_TAG_T in text3:
text4 = text3.split(CODE_TAG_T)
text_no_code += text4[1]
else:
text_no_code += text3
else:
text_no_code = text
# タグを除く
text_no_code = html.strip_tags(text_no_code)
# logging.debug(text_no_code)
logging.debug(funcname + ' end')
return text_no_code
# =======================================================
def keitaiso(text):
funcname = sys._getframe().f_code.co_name
logging.debug(funcname + ' start')
pure_text = ''
hinshi = ('名詞', '動詞', '形容詞', '副詞')
tok = Tokenizer()
tokens = tok.tokenize(text)
for token in tokens:
if token.part_of_speech.split(',')[0] in hinshi:
pure_text += ' ' + token.base_form
logging.debug(funcname + ' end')
return pure_text
# =======================================================
def makeWC(text):
funcname = sys._getframe().f_code.co_name
logging.debug(funcname + ' start')
wc = WordCloud(
width=WC_WIDTH, height=WC_HEIGHT,
background_color=BACKGROUND,
colormap=COLOR_MAP,
font_path=FONT_PATH,
prefer_horizontal=1,
stopwords=STOP_WORDS,
max_words=100,
min_font_size=6)
wc.generate(text)
wc.to_file(OUTPUT_FILE)
logging.debug(funcname + ' end')
# =======================================================
# メイン処理
text = parseLog()
text = removeHtml(text)
text = keitaiso(text)
makeWC(text)
コードはほとんどネットからのコピペです。
Bloggerのバックアップファイルから本文のみを抜き出した方法については、parseLog
関数のところに書いてありますので、よかったら参考にしてみてください。
あとは、コードを含めてしまうと横文字だらけのワードクラウドになってしまうので、codeタグで囲まれた部分は取り除くようにしています。
作ってみると、自炊を始めたこともあって「スキャン」の頻度が高かったです。
また、STOP_WORDS
に入れている(=入れないとまともなワードクラウドにならない)単語は安易に使ってしまう傾向があるようなので、意識的に避けなければ……とも思いました。
参考資料
大変お世話になりました!