Google Search Console の キーワードを元に、共起ネットワーク図が、描画できるか試してみた結果を記載します。
結論を書きますと、共起ネットワーク図かはわかりませんが、それっぽい図の描画はできました。
以下は、TfidfVectorizer
で作成した共起単語行列を元に出力した図になります。
共起ネットワーク図について
共起語については、以下に記載があります。
共起語SEOをもう一度解説してみる | 海外SEO情報ブログ
例えば、python
だったら django
とか flask
文書上一緒に現れてくることが多い単語と理解しました。
この共起語同士で関連性が高いものを集めて、その関連語に線を引いたものが共起ネットワーク図というのが、個人的な理解です。
一般的に、Web全体の中で共起するものなイメージですが、今回実装しているのは、Google Search Console の検索キーワードをinput に作成です。
これは、サイトが外[ここではGoogle]からどう見えているかを示すものかなと考えます。
処理の流れ
実装は python で作成しました。処理の流れは以下になります。
-
キーワード文字列を取得
Google Search Console からキーワードを取得します。
キーワードのデータは、Search Analytics for Sheets - Google スプレッドシート アドオン を使って取得、スプレッドシートの取得には、gspread
を使っています。 -
共起単語行列を作成する
キーワードをINPUTに、共起単語行列を作成します。
sklearn
の CountVectorizer と、TfidfVectorizer を使った処理を作成しました。 -
networkx で、ネットワーク図を描画
networkx
を使って、ネットワーク図を描画します。
前提
python の verison
% python -V
Python 2.7.10
必要なライブラリのインストール
スクリプトを動作させるのに必要なのは、以下になります。
実行する場合はあらかじめインストールお願い致します。
pip install networkx
pip install matplotlib
pip install sklearn
実装
以下作成したスクリプトになります。
2ファイルです。
-
search_console_plot_keyword_network.py
# -*- coding: utf-8 - import networkx as nx import matplotlib.pyplot as plt import search_console_classifier_utils as utils def __create_keywords_data(): keywords = [] # CSVからキーワードを取得 for line in utils.parse_report_tsv(): keywords.append(line) return keywords def __get_co_occurrence_matrix_from(keywords): # ----------------------------------------------------- # 以下、CountVectorizer で共起単語行列を作る # ---------------------------------- # from sklearn.feature_extraction.text import CountVectorizer # count_model = CountVectorizer(ngram_range=( # 1, 1), stop_words=utils.stop_words) # default unigram model # X = count_model.fit_transform(keywords) # # normalized co-occurence matrix # import scipy.sparse as sp # Xc = (X.T * X) # g = sp.diags(2. / Xc.diagonal()) # Xc_norm = g * Xc # import collections # splited_keywords = [] # for keyword in keywords: # splited_keywords.extend(utils.split_keyword(keyword)) # counter = collections.Counter(splited_keywords) # return Xc_norm, count_model.vocabulary_, counter # ----------------------------------------------------- # 以下、TfidfVectorizer で共起単語行列を作る # ---------------------------------- from sklearn.feature_extraction.text import TfidfVectorizer tfidf_vectorizer = TfidfVectorizer(ngram_range=( 1, 1), stop_words=utils.stop_words, max_df=0.5, min_df=1, max_features=3000, norm='l2') X = tfidf_vectorizer.fit_transform(keywords) # normalized co-occurence matrix import scipy.sparse as sp Xc = (X.T * X) g = sp.diags(2. / Xc.diagonal()) Xc_norm = g * Xc import collections splited_keywords = [] for keyword in keywords: splited_keywords.extend(utils.split_keyword(keyword)) counter = collections.Counter(splited_keywords) return Xc_norm, tfidf_vectorizer.vocabulary_, counter def main(): # ------------------------- # 1. キーワード文字列を取得 # ------------------------- keywords = __create_keywords_data() # ------------------------- # 2. 共起単語行列を作成する # ------------------------- Xc_norm, vocabulary, counter = __get_co_occurrence_matrix_from(keywords) # ------------------------- # 3. networkx で、ネットワーク図を描画 # ------------------------- # 3-1.初期ノードの追加 G = nx.from_scipy_sparse_matrix( Xc_norm, parallel_edges=True, create_using=nx.DiGraph(), edge_attribute='weight') # 3-2.nodeに、count にcount属性を設定 value_key_dict = {} for key, value in vocabulary.items(): count = counter.get(key, 0) nx.set_node_attributes(G, "count", {value: count}) value_key_dict.update({value: key}) # 3-3.エッジと、ノードの削除 # 出現回数の少ないエッジを削除 for (u, v, d) in G.edges(data=True): if d["weight"] <= 0.15: G.remove_edge(u, v) # 出現回数の少ないノードを除去 for n, a in G.nodes(data=True): if a["count"] <= 125: G.remove_node(n) # 3-4 ラベルの張り替え、from_scipy_sparse_matrix 設定時はラベルとして1,2,3 等の数値が設定されている G = nx.relabel_nodes(G, value_key_dict) # 3-5 描画のために調整 # figsize で 図の大きさを指定 plt.figure(figsize=(10, 10)) # 反発力と吸引力の調整 pos = nx.spring_layout(G, k=0.1) # ノードサイズの調整 node_size = [d['count'] * 20 for (n, d) in G.nodes(data=True)] nx.draw_networkx_nodes(G, pos, node_color='lightgray', alpha=0.3, node_size=node_size) # フォントサイズ、使用するフォントの設定 nx.draw_networkx_labels(G, pos, fontsize=8, font_family="IPAexGothic", font_weight="bold") # エッジの線の調整 edge_width = [d['weight'] * 2 for (u, v, d) in G.edges(data=True)] nx.draw_networkx_edges(G, pos, alpha=0.4, edge_color='c', width=edge_width) # 枠線の表示/非表示 on:表示 off:非表示 plt.axis("off") plt.show() if __name__ == '__main__': main()
-
search_console_classifier_utils.py
# -*- coding: utf-8 - import gspread from __builtin__ import unicode from oauth2client.service_account import ServiceAccountCredentials from sets import Set from sklearn.feature_extraction import text extra_words = Set(['name', 'not', 'the', 'usr', 'you', 'version', 'this']) stop_words = text.ENGLISH_STOP_WORDS.union(extra_words) key_file = "your_api_key.json" scope = ['https://spreadsheets.google.com/feeds'] def __check_stop_word(word): """ stop word のチェック 2文字以下の文字列を除去と、英語のstopwords を除去を行う """ if word in stop_words: return False if len(word) <= 2: return False return True def split_keyword(text): """ キーワードを区切り、stopwordsを除外する """ keywords = text.split(" ") return [keyword for keyword in keywords if __check_stop_word(keyword)] # report csv を parse する def parse_report_tsv(): lines = [] row_count = 1 credentials = ServiceAccountCredentials.from_json_keyfile_name( key_file, scope) gc = gspread.authorize(credentials) # スプレッドシート名は、Google Search Console Analyze シート名は[Merge] をOpen wks = gc.open("Google Search Console Analyze").worksheet("Merge") for line in wks.export(format='tsv').split("\n"): if row_count != 1: arr = line.split("\t") # キーワードカラムを取り出す lines.append(arr[1]) row_count += 1 return lines
説明
以下、スクリプトの処理の説明になります。
1. キーワード文字列を取得 について
キーワード文字列を、スプレッドシートから取得しています。
tsv そのままでも読み込めますが、個人的な好みでスプレッドシートから読み込むようにしました。
tsv でもcsv でもキーワードのリストを読み込めれば動作しますので、
スプレッドシートから読み取る必要がなければ、書き換えてください。
2. 共起単語行列を作成処理について
-
CountVectorizer と TfidfVectorizer
CountVectorizer、TfidfVectorizer でそれぞれ作成しました。
どちらもそれっぽい図が出力されたので、うまくいっているのではないかと思います。
CountVectorizer、TfidfVectorizer の説明は、テキスト分類問題その1 チュートリアル|ビッグデータ大学(β) がわかりやすかったです。
共起単語行列の作成方法は、python - word-word co-occurrence matrix - Stack Overflow を参考に作成しました。
CountVectorizer、TfidfVectorizer の出力結果もそれほど変わらないですが、個人的にはTfidfVectorizer の出力結果のほうがしっくりきました。
ただ、TfidfVectorizer に与えているパラメータはstopwords以外、意味もわからず設定しているのでそのあたりチューニングの余地はあるのかと思います。 -
stopword
stopwords は、キーワードとして検索されているのが英単語がほとんどだったので、sklearn.feature_extraction.text の ENGLISH_STOP_WORDS と 個人的に気になった単語を以下のようにunion してそれを使用しました。
extra_words = Set(['name', 'not', 'the', 'usr', 'you', 'version', 'this']) stop_words = text.ENGLISH_STOP_WORDS.union(extra_words)
-
メソッドの戻り値
戻り値についてですが、共起単語行列Xc_norm
と ラベルと単語の紐付け辞書vocabulary_
以外に、単語数を返すようにしました。
これは、後続処理でNode の大きさを単語数により指定したためです。
CountVectorizer、TfidfVectorizer の 戻りで代替となる値をとれればそれを使うのですが、見つけられませんでした。
3. networkx で、ネットワーク図を描画
[Python]NetworkXでQiitaのタグ関係図を描く - Qiita を参考に作成しました。
変えたところについて説明します。
-
3-1.初期ノードの追加
Xc_norm
を Loop で設定しようかと思ったのですが、なかなかcsr_matrix を変換する実装サンプルが見つからず、メソッドとしてfrom_scipy_sparse_matrix
が存在したので、そちらを使うようにしました。
以下を参考にしました。
python - Transform csr_matrix into networkx graph - Stack Overflow
from_scipy_sparse_matrix — NetworkX 1.11 documentation -
3-2.nodeに、count にcount属性を設定
Nodes に 文字のカウント数設定しています。
Nodeサイズをこれを元に設定します。
ここでついでに、value と、key を 入れ替えています。count = counter.get(key, 0) nx.set_node_attributes(G, "count", {value: count})
value_key_dict.update({value: key})
-
3-3.エッジと、ノードの削除
出現回数の少ない、エッジと、ノードの除去をしています。
ここは、サイトのアクセス数で変わってくるのかと思います。
# 出現回数の少ないエッジを削除 for (u, v, d) in G.edges(data=True): if d["weight"] <= 0.15: G.remove_edge(u, v) # 出現回数の少ないノードを除去 for n, a in G.nodes(data=True): if a["count"] <= 125: G.remove_node(n)
-
3-4 ラベルの張り替え
value と key を入れ替えた dictionary を使って、ノードに名称を設定します。
G = nx.relabel_nodes(G, value_key_dict)
説明は以上です。
TODO
動くものを実装することはできました。 以下 TODO 事項になります。
-
click 数とか、impression数を勘案して図に反映する
キーワードのclick 数、impression数 を勘案した描画ができていないので、そのあたりを勘案して描画したいです。 -
グラフツールを変えてみる
調べていたらいろいろ存在するということがわかりました。
graph-tool: Efficent network analysis with python や Cytoscape / cyRESTとpy2cytoscapeを用いたIPython Notebook上でのグラフ解析と可視化 Part 1 - Qiita でも、ネットワーク図が描けるようなので、
他のグラフツールで描いてみたいなと思いました。
参考
以下、文中に登場する以外で参考にした記事になります。
以上です。
コメント