Google Colaboratory で Qiita:Team の全投稿を取得して、可視化したり、人気ランキングを作ってみる

Kaizen Platform で Product Manager / Engineering Group Manager をしている @takus です。

Kaizen Platform では全社の情報共有ツールとして Qiita:Team を利用しています。Qiita:Team は API が公開されていて投稿した記事などを取得することができるため、Google Colaboratory で全記事を取得して投稿の傾向を可視化したり、人気ランキングを作ってみたりしたので、その話を紹介したいと思います。

TL;DR

  • Kaizen Platform は全社員が積極的に情報発信をしながら仕事をしている
    • 積極的な情報発信は良い面が多いが、情報過多になる悪い面もある
  • 情報の流通状況を可視化してみることでよりよい情報共有の仕組みに改善中
    • 計測できないものは改善できない
    • 全ては公開できないけど一部の内容を紹介
  • Google Colaboratory は情報収集と可視化を気軽にするために便利
    • 今なら無料で使えるし、複数人でコラボレーションもしやすくてオススメ

Kaizen Platform と Qiita:Team

オープンさ

オープンさ: 自らの仕事 (プロセス・成果) をオープンに。 情報は閉じ込めてはいけない。 どこにあるかわからない/誰も読んでないドキュメントには価値はない。

Kaizen Platform, Inc. エンジニア行動指針 – Kaizen Platform のモノ作り – Medium にも オープンさ が記されてるように、会社として新しい働き方を作るということに挑戦するにあたって、Kaizen Platform は情報共有・透明性を大事にしながら、 CEO からエンジニア、カスタマーサクセス、バックオフィスのメンバーなど、皆が積極的に情報発信をしながら仕事をしています。

Qiita:Team

f:id:kaizenplatform:20180425231517p:plain

その中でも、以前から情報共有ツールとして利用している Qiita:Team には様々な部署から様々な情報がもたらされ、毎日たくさんの人が情報発信をしています。これは、積極的な情報発信によって部署を超えたコラボレーションや議論が非同期で進められるメリットがある一方で、情報過多になって大切な情報を見落としたりするデメリットもあり、全てがうまくいってるわけではなく、情報の流通状況を可視化してみたりすることでよりよい情報共有の仕組みへの改善を進めています。

その取り組みの一貫として、Google Colaboratory で Qiita:Team の全記事を取得して投稿の傾向を可視化したり、人気ランキングを作ってみたりしたので、その方法について解説していきます。

Google Colaboratory の環境準備

f:id:kaizenplatform:20180426001351p:plain

Google Colaboratory は Google が機械学習の教育や研究用に提供している Full Managed な Jupyter Notebook 環境です。2018/04/25 時点では、リサーチプロジェクトということで無償で利用することができます。

使い始めるのは笑えるくらい簡単で Google Colaboratory にブラウザでアクセスするだけです。コードスニペットなども充実しているので、pip によるライブラリのインストール方法や、Google Drive との連携などを簡単に行えます。ここでは使い方の説明はしませんが、そのあたりが知りたい場合は Google Colaboratory事始め - Qiita などをご覧ください。

記事取得

Qiita API v2 の GET /api/v2/items で記事を取得した上で、1 行 1 投稿の JSON 形式でファイルに保存していきます。

team = 'YOUR_TEAMNAME'
access_token = 'YOUR_ACCESS_TOKEN'

start_date = '2018-01-01'
end_date = '2018-03-31'

import sys
import re
import json

import pandas as pd
import numpy as np

!pip install -q qiita_v2 retry
from qiita_v2.client import QiitaClient
from qiita_v2.exception import QiitaApiException
from retry import retry

c = QiitaClient(team=team, access_token=access_token)

@retry(delay=1, backoff=2, max_delay=8)
def list_items(yyyymm, page=1, per_page=100):
  print('fetching yyyymm:{} page:{}'.format(yyyymm, page), file=sys.stderr)
  r = c.list_items(
    params={
      'page': page,
      'per_page': per_page,
      'query': 'created:{}'.format(yyyymm),
    })
  return r
        
for dt in pd.date_range(start=start_date, end=end_date, freq='MS', tz='Asia/Tokyo'):
  yyyymm = dt.strftime('%Y-%m')
  filename = 'qiita-{}.json'.format(yyyymm)

  with open(filename, 'w', encoding='utf8') as f:
    posts = list_items(yyyymm, page=1)
    m = re.search(r"\?page=(\d+)", posts.link_last)
    if m:
      last_page = int(m.group(1)) + 1
    else:
      last_page = 2
    
    for page in range(1, last_page):
      posts = list_items(yyyymm, page=page)
      for post in posts.to_json():
        t = json.dumps(post, sort_keys=True, ensure_ascii=False) + "\n"
        f.write(t)

記事取得における工夫

全記事を取得するにあたって 2 件ほど問題が起きたので下記のような対応をしています。ページングが 100 ページまでしか遡れない問題は悩みの種でしたが、query で絞り込みを入れることで検索結果を 10,000 件以内に絞り込むことで全取得が可能になります。別のドキュメント管理ツールにデータを移行するような必要がある場合は覚えておくとよいと思います。

  • API アクセスが一定確率でエラーになる
    • 一定間隔でリトライすることで解決
  • Qiita API v2 のページングが 100 ページまでしか遡れない
    • 記事が 10,000 件を超えると途中までしか遡れない
    • query で created:2017-01 など指定して月毎に取得することで解決

記事を Dataframe として読み込み

JSON 形式で保存されているファイルを、Jupyter Notebook 上で扱いやすい Dataframe 形式で読み込みます。

import glob
import pandas as pd
import numpy as np

# Load JSON as dataframe
list = []
for f in glob.glob('*.json'):
  list.append(pd.read_json(f, lines=True))
df = pd.concat(list)

# Drop unused columns
df = df.drop(['group', 'body', 'rendered_body', 'likes_count', 'page_views_count'], axis=1)

# Flatten user & tags
df = df.assign(user = df.apply(lambda x: x['user']['id'], axis=1))
df = df.assign(tags = df.apply(lambda x: ' '.join(map(lambda y: y['name'], x['tags'])), axis=1))

# Sort by created_at
df = df.sort_values(by="created_at")

df.tail()

df.tail() すると Dataframe の一部が確認できます。

f:id:kaizenplatform:20180425231803p:plain

記事の投稿状況を可視化

2016 年以降の月別の記事投稿数を集計して時系列の変化をみてみます。

df_by_month = df\
  .groupby([ df['created_at'].dt.year, df['created_at'].dt.month])\
  .agg({
    'url': {'items_count': np.count_nonzero},
    'user': {'users_count': lambda x:len(x.unique())},
    'reactions_count': {'reactions_count': np.sum},
    'comments_count': {'comments_count': np.sum},
  })
df_by_month.index.names = ['year', 'month']
df_by_month.columns = df_by_month.columns.droplevel(0)
df_by_month = df_by_month.reset_index()

df_by_month\
  .plot(kind='bar', x=['year','month'], y='items_count', color="red", alpha=0.6, figsize=(12,2))

今回は matplotlib で棒グラフを作成してみました。 2016 年は会社の状況があまりよくなかったと聞いていましたが、それを顕著に示すような結果になりました。

f:id:kaizenplatform:20180425231830p:plain

人気記事の月間ランキング作成

最後に記事毎のリアクション数を集計して月間人気記事ランキングを作成して、Qiita:Team にそのまま貼れる Markdown 形式で出力してみます。

yyyy = 2018 
mm = 1

monthly_ranking = df.where(
    (df['created_at'].dt.year == yyyy) & (df['created_at'].dt.month == mm)
  )\
  .sort_values(by=['reactions_count'], ascending=False)\
  .head(20)\
  .reset_index()

print("### {} 年 {} 月".format(yyyy, mm))
print("| Rank | Reactions | Title | Author |")
print("|:----:|:---------:|:------|:-------|")
for index, row in monthly_ranking.iterrows():
  print("| {} | {} | {} | @{} |".format(index + 1, row['reactions_count'], row['url'], row['user']))
print()

これを Qiita:Team に投稿することで下記のような月間ランキングを簡単に作成することができます。 年初なので CEO/CTO の年頭所感、新入社員の自己紹介などが人気だったみたいですね。

f:id:kaizenplatform:20180425232235p:plain

まとめ

他にも、

  • 曜日・時間帯でヒートマップを作成してどんな時間帯に投稿が多いか?
  • ユーザの部署情報を用意して部署毎の投稿状況や投稿者数に違いがあるか?
  • マネージャーとメンバーで投稿数にどのような違いが出るか?

など、色々と観察してみると見えてくることが多いので Google Colaboratory で簡単に分析をしてみて、継続的にみたいものは BI ツールの Chartio で可視化していきたいなと思っています。