キーワードで再発見した「こち亀」の魅力
こち亀は40年間も長期連載していた、思い出の詰まった作品です。
そんなこち亀へのリスペクトと私自身の復習のために、こち亀を徹底的に分析してみました。意外な発見もたくさんあり、こち亀を新たな視点から楽しめたデータ分析となりました。
具体的な分析内容としては、「ワードクラウド」とよばれる頻度の高いキーワードを大きく表示する視覚化技法を用いて、各時期の象徴的なキーワードを抽出する形をとりました。なお、分析過程でAmazon単行本ページのdescriptionを抽出し、自然言語処理をかけてみました。
(※)Amazon規約を精査しまして、非ログイン状態であればスクレイピングは問題ないことを確認しています。
1~40巻:1977/7/9 ~ 1986/5/9
この時期は、「暴走」「拳銃」「追撃」など尖ったワードが並びます。「本田」君の最盛期で、初期は登場が多かったことが示されています。単行本の表紙も、銃を構えたりバイクに乗る絵柄が他の時期よりも目立ちます。
41~80巻:1986/7/10 ~ 1993/6/4
「浅草」「神輿」「上野」というキーワードが並ぶように、お祭りや下町の描写が比較的多い時期でした。50巻あたりから表紙絵が劇画寄りから優しいトーンに変わっており、柔らかな作風への変化を感じます。
81巻~120巻:1993/6/4 ~ 2000/7/4
「麗子」「マリア」が頻出しており、2大ヒロインの全盛期を感じさせます。「婦警」というキーワードも目立つように、100~120巻は女性が表紙を飾る機会が多くなりました。その中で、マッチョ揃い踏みの108巻表紙が逆に目を引きます。「ゲーム」「たまごっち」など娯楽の変化も窺えます。
121~160巻:2000/9/4 ~ 2008/6/4
ニューヒロイン「纏」の登場が目覚ましいです。両さんが最も「結婚」に近づいたのもこの時期でした。「大阪」「京都」の話題も多く、関西へのお出かけがとても活発でした。これまで見慣れない人物名が増えているように、新キャラが多く登場するにぎやかさもこの時期の特徴です。
161~201巻:2008/9/4 ~ 2021/10/4
「部長」が堂々の最頻出キーワード。フィナーレも近づくこの時期は、最初期からみられた部長とのお馴染みの掛け合いも見せてくれました。「夏」「海」も注目すべきポイントで、季節描写の豊かなこち亀の中でも、やはり真っ赤な太陽に白い砂浜、青い海が、そのド真ん中にくるのではないでしょうか。
技術ハイライト
これまでのアウトプットには、下記の手順で進めました。デバイスはMacです。
最初はローカル環境でMeCabを試しましたが、循環importの不具合が解消されず、仮想環境を立ててMeCabを実行する方針に切り替えました。
(1)Dockerで仮想環境構築(2)Selenium仮想環境にSafariブラウザ立ち上げ(3)WebDriverでスクレイピング(4)Docker仮想環境でのJupyterNotebook上で、自然言語処理(MeCab)とワードクラウド生成
(1)Dockerで仮想環境構築
Dockerの構築はこちらをご参照ください。
【入門】はじめての Docker Desktop for Mac のインストールと CentOS の仮想環境構築のセットアップ
Dockerfile とdocker-compose.yml をローカルのホームディレクトリに設置
FROM python:3.9
WORKDIR /app
SHELL ["/bin/bash", "-c"]
RUN apt-get update &
RUN pip install --upgrade pip
RUN pip install notebook
RUN pip install selenium
RUN pip install numpy
RUN pip install pandas
RUN pip install mecab-python3
RUN pip install unidic-lite
RUN pip install wordcloud
version: '3'
services:
app:
build: .
volumes:
- ./:/app
ports:
- 8888:8888
tty: true
コンテナを起動します
docker-compose up -d
起動確認はこちら
docker-compose ps
(2)Selenium仮想環境にSafariブラウザ立ち上げ
Seleniumの基本概念は、こちらがわかりやすいです。
Selenium Gridの環境を構築する(docker-compose利用)
selenium-server-standalone-3.5.3.jar をダウンロードし、ローカルのホームディレクトリに設置します。
https://selenium-release.storage.googleapis.com/index.html?path=3.5
nodeConfig.jsonを作成し、ローカルのホームディレクトリに設置します。
{
"capabilities":
[
{
"platform": "MAC",
"version": "10.0",
"browserName": "safari"
}
],
"maxSession": 5,
"hub": "http://localhost:4444/grid/register",
"port": 5051,
"register": true
}
Selenium Grid Server(Hub) を立てます。
※javaが入っていない場合はインストール
java -jar selenium-server-standalone-3.5.3.jar -role hub
ここでは、http://10.70.248.78 がHubに指定されているので、
追加する端末(Node)はこの Hub URLに登録します。
次に、端末(Node)を立てます。
java -Dwebdriver.safari.driver=/usr/bin/safaridriver -jar selenium-server-standalone-3.5.3.jar -role node -nodeConfig nodeConfig.json -hub http://192.168.11.2:4444/grid/register
そして、ターミナルの Nodes should register to … にあるURLホスト名部分を利用して、ローカルブラウザURLに http://10.70.248.78:4444/grid/console と入力。
Selenium仮想環境にSafariブラウザが立ち上がりました。
※ Hub URLのホスト名は実行タイミングで変化することがあります。
※ Safariブラウザを選択した理由は、(1)WebDriverがデフォルトで用意されており、ブラウザのバージョンをダウングレードしてWebDriverに合わせる必要がないこと(2)スクレイピング時の指定が簡単であること
(3)Docker仮想環境にJupyterNotebookを立ち上げ、スクレイピング実行
連載一覧ページから、各単行本のdescriptionを取得し、dataframeに格納します。
まずは、Docker仮想環境で jupyter notebook を立ち上げます。
以下のコマンドでjupyter notebookを起動します。
jupyter notebook --port=8888 --ip=0.0.0.0 --allow-root --NotebookApp.token=''
起動できたら、ローカルブラウザで http://localhost:8888/ にアクセスすると、
Docker環境で立ち上げた jupyter notebookを操作することができます。
スクレイピング実行前に、ローカルのSafari設定について、開発 > リモートオートメーションを許可 を設定。
jupyter notebook上で下記のスクリプトを回し、WebdriverでSelenium仮想環境のSafariにスクレイピングを実行させます。
import sys
from selenium import webdriver
from time import sleep
import numpy as np
import pandas as pd
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
#Safariドライバ起動
SafariDriver = webdriver.Remote(command_executor='http://10.70.248.78:4444/wd/hub',
desired_capabilities=DesiredCapabilities.SAFARI)
SafariDriver.get('https://www.amazon.co.jp/dp/B0758FFXPZ')
#スクレイピング実行
#cssセレクタはサイトコード変更によって有効でなくなる可能性があります。
css_selector = "li#seriesAsinListShowNextPage > span.a-list-item > a.a-link-normal"
for i in np.arange(20):
element = SafariDriver.find_element(By.CSS_SELECTOR, css_selector)
SafariDriver.execute_script("arguments[0].scrollIntoView();", element)
sleep(1)
SafariDriver.find_element(By.CSS_SELECTOR, css_selector).send_keys(Keys.ENTER)
sleep(2)
dataset = pd.DataFrame(columns=['id', 'description'])
elem = SafariDriver.find_elements(By.CSS_SELECTOR, "div.a-expander-content > div.a-section > span.a-size-base")
for i, e in enumerate(elem):
dataset = dataset.append({'id': i, 'description': e.text}, ignore_index=True)
(4)Docker仮想環境でのJupyterNotebook上で、自然言語処理とワードクラウド生成
日本語フォントをダウンロード
sudo apt install fonts-ipaexfont
出力ワードクラウド画像を確認しつつ、特徴が際立つように除外キーワードを手動で入れることを繰り返して改善していきます。
# 除外キーワード
trim_words_ = '【ページ数が多いビッグボリューム版!】|編|収録|スル|巻|他|イル|イク|前|ナル|クル|こと|ハジメル|ミル|ススメル|メザス|ナイ|アル|シマウ|'\
'イウ|ヤル|ツレル|フクム|ノル|ツケル|もの|ため|デル|キク|少年|ジャンプ|増量|ヒラク|エラブ|カウ|カク|シル|両|派出|所|やつ|亀|津|勘吉|ハタシテ|オシエル'
def trim_words(text):
text = re.sub(trim_words_, '', text)
return text
dataset['description'] = dataset['description'].apply(trim_words)
import re
import MeCab as mc
def strip_CRLF_from_Text(text):
plaintext = re.sub('([ぁ-んー]+|[ァ-ンー]+|[\\u4e00-\\u9FFF]+|[ぁ-んァ-ンー\\u4e00-\\u9FFF]+)(\n)([ぁ-んー]+|[ァ-ンー]+|[\\u4e00-\\u9FFF]+|[ぁ-んァ-ンー\\u4e00-\\u9FFF]+)',
r'\1\3',
text)
plaintext = plaintext.replace('\n', ' ').replace('\t', ' ')
return plaintext
def mecab_wakati(text):
t = mc.Tagger()
node = t.parseToNode(text)
sent = ""
while(node):
if node.surface != "":
word_type = node.feature.split(",")[0]
if word_type in ["名詞"]:
sent += node.surface + " "
if word_type in [ "動詞", "形容詞","副詞"]:
sent += node.feature.split(",")[6] + " "
node = node.next
if node is None:
break
return sent
dataset['words'] = dataset['description'].apply(strip_CRLF_from_Text)
dataset['words2'] = dataset['description'].apply(mecab_wakati)
book_group = (dataset['id'] + 1) // 40
dataset_ = dataset.assign(book_group=book_group)
for i in np.arange(5):
zero_group = dataset_.query('book_group==' + str(i))
words_set = ''
for words in zero_group['words2']:
words_set = words_set + ' ' + words
words_set = re.sub(trim_words_, '', words_set)
# 日本語フォントにパスを通す
zero_group_wc = WordCloud(max_font_size=60, font_path='/usr/share/fonts/opentype/ipaexfont-gothic/ipaexg.ttf').generate(words_set)
zero_group_wc.to_file("zero_group_wc_" + str(i) + ".png")