Pythonのpyvisライブラリーでキーワードの共起語ネットワーク図を作成して、SEO対策へのキーワードに優先度をつけてみた。

最近、知人と介護関連のメディアを作成しています。その中でSEO対策としてキーワードを列挙したり、キーワードごとに競合分析をしたりしておりました。とあるツールを使って介護に関連のある検索キーワードをCSVファイルでダウンロードしたところ、300,000行ほどになりました。人間の手ではとてもまとめられないので、「共起語ネットワーク」を作成するべく、プログラムを組みました。今回は、その奮闘記をお伝えします。

Harumaki
Harumaki

初めてPythonを使って分析作業です!

SEO対策の為のキーワード分析

メディアを作成するにあたり、キーワード分析は必要でした。記事を執筆する際に、キーワードが固まっていないと方向性の定まっていない記事になってしまうからです。狙うキーワードを列挙するために、ラッコキーワードやキーワードプランナーを使用してビッグキーワードや介護に関連のある検索キーワードを調べました。

それだけでは十分ではないと考え、有料ツールを使用して、競合メディアのデータも取得しました。競合メディアが特定のキーワードで何位なのか?それぞれの検索キーワードの月間ボリューム(月にどれくらい検索されるかを示す数値)などをデータとして入手しました。

これらのデータを入手したわけですが、そのデータ量は非常に多く、Numbersで開くと300,000行近くのデータになっておりました。以下はイメージです。

KeywordCurrent position
SERP featuresVolumeKDCPCPrevious trafficCurrent trafficTraffic changePrevious position
介護 保険4Sitelinks,1100012.1227663910
介護 費用51Featured 450030.013250186
高齢者 保険3Featured 13000117.342484723
老人ホーム 費用71Featured 80001201343323
SEOツールを使って得たCSVファイル

Numbers内で重複や並び替えを行おうとするとめちゃくちゃ重い。。。macbookPro で重複を調べるのに丸々6日かかりました。恐ろしい。スプレッドシートも試みましたがセル数が多すぎで制限がかかり使えませんでした。

結果、データベースをローカル環境に立ち上げることにしました。MySQLの中にCSVをインポートしてSQLで抽出を試みるとなんと数秒で完了。データベース凄すぎです。データベースのありがたみを体感し、重複や並び替えに成功しました。データが少し整理されました。

大量のデータを人間が読み取るのは限界がある

並び替えましたが、データはいまだに多いです。300,000行のデータ人間が一行一行見て、自分のメディアに関係のあるもの、ないものを判断するのは至極困難です。

何かいい方法はないかなと考えました。googleで検索すると「共起ネットワーク」という言葉があることを知りました。

これは以下の意味です。

出現パターンの似通った共起の程度が強いを線で結ん だネットワークとして描き、が互いにどのように結びついているか読み取れるものである。

https://miyazaki-mu.repo.nii.ac.jp/?action=repository_action_common_download&item_id=1236&item_no=1&attribute_id=21&file_no=1#:~:text=%E5%85%B1%E8%B5%B7%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%A8%E3%81%AF,%E5%A0%B4%E5%90%88%E3%81%8C%E3%81%82%E3%82%8B%E3%81%A8%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%80%82

イメージはこんなの↓です。(今回、私が作成したモノになります。)

この共起語ネットワークなるものを使用すれば、膨大な単語の中から優先するべきキーワードを発見できると思いました。

線の大きなもの同士が強く結びついています。上記でしたら「高齢者」を中心に考えると「認知症」「レクリエーション」「老人ホーム」などに強い結び付きがありました。ですので、これらのキーワードを優先して対策すれば良いという解釈ができます。(戦略にはよるのでしょうが。。。)

CSVファイルをPythonで読み、共起語ネットワークを作成しました。Python初経験の私でもできた方法をご紹介します。

Pythonの「pyvis」ライブラリーを使用するための環境準備

pyvisとはPythonライブラリで、ネットワーク図の生成してくれます。pyvisを利用するために、まずはPythonの環境を整えます。macであれば、Pythonがすでにプリインストールされています。しかし調べてみるとPython Japanさんがこんなことを言っておりました。

macOS Catalina には、Python2.7とPython3.7がプリインストールされています。しかし、プリインストールされてるバージョンはかなり古く、利用はおすすめできません。別途、あたらしいPythonをインストールするのが一般的です。

https://www.python.jp/install/macos/index.html

どうやら新しくインストールするのが一般的なようですね。私は知らずに「Python既に入ってるやーん。ラッキー」となり、そのまま作業を進めました。今回の作業では別に変なエラーはなかったので別の機会にアップデートしておこうかなと。

自分のPCにPythonは入っているかの確認ですが、コマンドツールで

$ python  -V

と打てばわかります。

私の場合は

Python 2.7.16

と却ってきてました。

Pythonの環境ができたら次はpyvisライブラリーを導入します。JavaScriptはnpmやyarn、PHPはcomposerといったように、当然Pythonちゃんにもパッケージ管理ツールがあります。pipというらしいです。無論、今回ははじめてPythonを触ったので初めて知りました。pipの導入にはこちらの「【Python】macOSでpipを使えるようにする」を参考にしました。

$ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py

まずは適当な所にディレクトリーを作り、その中でコマンド↑を実行してget-pip.pyファイルを取得。そのあと、以下のコマンドでpipが使えるようになります。

$ python3 get-pip.py

pipを入手したら次はpyvisをインストールします。

$ pip install pyvis

上記のコマンドでpyvisがインストールされます。

以上でpyvisを使える準備が整いました。

余談ですが、pythonのコードを実行する方法を私は知りませんでした。調べて分かったのですが、適当なhoge.pyファイルを作成して中身コードを記述して、

$ python3 hoge.py

とコマンドを打てば、hoge.pyが実行されるんですね。(※.pyはPythonファイルです。)

シンプルっすね。試しにハローワールドをしてみました。

print("Hello Worldです〜〜〜〜")
$ python3 hoge.py
Hello Worldです〜〜〜〜

実際にpyvisを使ってみる

さっそくpyvisを使ってみます。今回参考にしたのは以下の2つのサイトです。

・ pyvisの公式チュートリアル https://pyvis.readthedocs.io/en/latest/tutorial.html

・pyvisとCSVを使ったコード例 https://gist.github.com/jshirius/495fd41acbb91291d75a9f414e63c4e4#file-ipynb

また、コードの全体を貼り付けます。

### 機能概要
# キーワード同士の関連性を調べる
# キーワードの重複度を調べる
# 優先度の高いキーワードを調べる

# 参考にした記事 
# pyvis公式チュートリアル https://pyvis.readthedocs.io/en/latest/tutorial.html
# pyvisとCSVを使ったコード例 https://gist.github.com/jshirius/495fd41acbb91291d75a9f414e63c4e4#file-ipynb
# グラフの設定のドキュメント https://pyvis.readthedocs.io/en/latest/documentation.html

from pyvis.network import Network
import requests
import json
import pprint
import datetime
import pandas as pd
import os 
import gspread
import math
import pyvis
import itertools
import collections

# CSVファイルの読み込み 使用したCSVはセパレーターがタブではなくて、カンマだったので、sep=','で指定。
# 読み込みたいファイル名に編集してください
df = pd.read_csv("keywords.csv", sep=',') 
print('keywords.csvの大きさ:',len(df))

# キーワードの一覧を取得する ここを読み込みたいカラム名に編集してください
keywords = df["Keyword"].values.tolist()

# csvファイルの中の検索キーワードに対して、単語単位で切り分けて格納する
# 例えば「介護 脳トレ」「介護 レクリエーション おもしろい」と続けば、[["介護","脳トレ"],["介護","レクリエーション","おもしろい"]]のように格納
keyword_list = []
for i in keywords:
    w =i.split(" ")
    keyword_list.append(w)

# ネットワーク描画用の関数(可視化のために使用する)
def keyword_network():
    got_net = Network(height="1000px", width="75%%", bgcolor="#FFFFFF", font_color="black", notebook=True)

    got_net.force_atlas_2based() #描画のグラフの種類を変更可能
    got_data = pd.read_csv("network.csv")
    print('network.csvの大きさ:', len(got_data))
    
    sources = got_data['first'] # first
    targets = got_data['second'] # second
    weights = got_data['count'] # count


    edge_data = zip(sources, targets, weights)
    for e in edge_data:
        
        # 重複が5以下は描画しない 数が多くでややこしくなるため
        if e[2] <= 5:
            continue
        
        src = e[0]
        dst = e[1]
        w = e[2]
        
        got_net.add_node(src, src, title=src)
        got_net.add_node(dst, dst, title=dst)
        got_net.add_edge(src, dst, value=w)

    neighbor_map = got_net.get_adj_list()

    # 【公式コメント】add neighbor data to node hover data
    for node in got_net.nodes:
        node["title"] += " Neighbors:<br>" + "<br>".join(neighbor_map[node["id"]])
        node["value"] = len(neighbor_map[node["id"]])

    
    got_net.show_buttons() #設定項目を全て表示する
    
    return got_net


sentences = keyword_list

# ここで全ての単語の組み合わせを、計算しているよう....。
sentence_combinations = [list(itertools.combinations(sentence, 2)) for sentence in sentences]
# ※ combinations('ABCD', 2) → AB AC AD BC BD CD
# print()で見る限り、例えば上記の["介護","レクリエーション","おもしろい"]であれば、
# [('介護', '高齢者レクリエーション'), ('介護', 'おもしろい'), ('レクリエーション', 'おもしろい')]に変わっている。

# 単純に各配列の中の2つの要素を並び替えて置き換えているだけのよう
# ('介護', '高齢者レクリエーション')と('高齢者レクリエーション','介護')は同じとしてカウントするため?
sentence_combinations = [[tuple(sorted(words)) for words in sentence] for sentence in sentence_combinations]

target_combinations = []
for sentence in sentence_combinations:
    target_combinations.extend(sentence)
    
ct = collections.Counter(target_combinations)
common_list = ct.most_common() # 単語とそれの出現回数をセットにして格納 例:[('介護', '高齢者レクリエーション'), 120), (('介護', 'おもしろい'), 24), (('レクリエーション', 'おもしろい'), 32]とか


# ※collectionsとmost_common()の使い方メモ
# l = ['a', 'a', 'a', 'a', 'b', 'c', 'c']
# c = collections.Counter(l)
# print(c)
# Counter({'a': 4, 'c': 2, 'b': 1})
# print(c.most_common())
# [('a', 4), ('c', 2), ('b', 1)]


# 組み合わせの結果から、first:初めの単語 second:2つめの単語 count:出現回数 を取り出してcsvにする
df = pd.DataFrame([{'first' : i[0][0], 'second' : i[0][1], 'count' : i[1]} for i in common_list])
df.to_csv('network.csv', index=False)


# pyvisを使って共起語のネットワークを作成する
got_net = keyword_network()
got_net.show("network.html")

汚いPythonコードかもしれませんが、そこはご愛嬌でお願いします。

大きな流れとしては以下です。

  1. 必要なパッケージ(や関数も?)を読み込む
  2. CSVファイルを読み込ませる
  3. pivisのNetworkクラスが働いて、いい感じのネットワーク図(htmlファイル)を生成
  4. htmlファイルを開いたらいい感じのネットワーク図ができている
  5. ハッピー!

です。

基本的には、csvファイル名と、そのファイルのカラム名を書き換えるだけで動くと思います。

結果として次のようなhtmlファイルが生成されます。初めに紹介した画像と同じです。

出力されたネットワーク図

最後にPython初心者の私が理解するのに悩んだことポイントや、pivisのNetworkクラスの挙動を理解するのに苦労した箇所をご紹介します。

悩んだり、理解に苦労したポイント

ここでは悩んだり、理解するのに苦労したりした箇所をご紹介します。半分ネタです。

コマンドでpython3を使っておらず少しハマる

.pyファイルを実行する時に、

$ python hoge.py

としてました。しかしこの時にpythonファイル内でimportしているpyvisがありませんよーというエラーがでてました。「いやいや、ちゃんとinstallしましたよ」と思っておりましたが、python3コマンドでget-pip.pyを取得していたので?、python3コマンドで実行したらきちんとimportしてくれました。

$ python3 hoge.py

おそらく保存される場所が内部でpython3とpythonで違うのかな?最新のpythonをインストールしていれば問題なくpythonコマンドでもいけるようです。macのプリインストールされているpythonを使ったのでこのようなことになったのかなと思っています。

必要なパッケージがなぜか入っていない

pyvisを入れた時点で必要なパッケージは入っていると思っていましたが、いくつかインストールされていないパッケージがありました。その都度、pip installしました。

$ pip install hogehoge

私の場合、requestsやpandasが入ってなかったです。

CSVファイルには方言があり、読み込むのにハマった

CSVファイルには方言があるようです。具体的には、カンマで区切る場合とタブで区切る場合があるようです。私はCSV(Comma-Separated Values)は名前の通りカンマで区切るものだと思っていましたが、タブで区切る場合があるらしいです。詳しくは以下です。

CSVは「,」(カンマ)区切りが基本ですが、(タブ)で区切る場合もあります。これを特にTSV(Tab-Separated Values)と呼びます。TSVはよく利用されますが、データ内にを含まないことが前提です。もし含む場合は、削除もしくは置換を行います。

https://codezine.jp/article/detail/2364

参考にさせていただいたサンプルコードがタブで区切られたCSV前提で書かれていたので、そこを修正する必要がありました。

df = pd.read_csv("keywords.csv", sep=',') 

sep=’ , 'とすることでカンマ区切りで値を読み込んでいます。

ネットワーク図上で設定画面がでなくてハマった

こちらの画像の右側には、設定項目があります。リアルタイムでネットワーク図のUIを編集することができます。しかしこれをがなかなか出てこなかったです。いろいろ試みた結果、画面の外側に表示されていて確認できていなかっただけという、あるあるパターンでした。

got_net = Network(height="1000px", width="75%%", bgcolor="#FFFFFF", font_color="black", notebook=True)

ここでインスタンスを生成しますが、ネットワーク図のwidthを100%とかにしていると設定画面の表示をtrueにしていても見切れてしまうので注意です。またデフォルトでは設定画面は表示されません。なので、

got_net.show_buttons() #設定項目を全て表示する

とすることで、表示できます。また、必要な機能だけをフィルターして表示することも可能です。

got_net.show_buttons(filter_=['nodes', 'edges', 'physics']) #配列の中の3種類のフィルター これだと全て表示される 表示したいものだけ記述すればOKです

アルゴリズムの理解が難しい

ネットワーク図を描画するための元のデータの作成

sentence_combinations = [list(itertools.combinations(sentence, 2)) for sentence in sentences]
sentence_combinations = [[tuple(sorted(words)) for words in sentence] for sentence in sentence_combinations]

配列の中にある配列の中にある配列の中にある配列の・・・・。とトレースが難しかったです。そして並び替え。理解せずにpyvisを使うのが少し気持ち悪かったのでアルゴリズムを理解しようと努めました。紙に書いてトレースしました。笑

久しぶりに理数系の脳を使った気がする。。これがPythonか。。。?!笑

最後に

今回は、Pythonのpyvisライブラリーでキーワードの共起語ネットワーク図を作成してみました。結果、SEO対策へのキーワードに優先度をつけることができたのかなと思います。

少しでも参考になればと思います。

最後まで読んでいただきありがとうございました。