meowの覚え書き

write to think, create to understand

Speaker DeckのPageViewをロギング&可視化する仕組みをコストを抑えて作った

f:id:meow_memow:20210915194634j:plain

この記事では、Speaker Deck上にアップロードしたスライドのページビュー(以下PV)を1日毎に収集し、日付ごとのPVをグラフとして可視化する仕組みをPythonで開発したことについて説明する。

目次


スポンサードリンク

背景

私が勉強会の発表スライドを共有する時、サービスはSpeaker Deckを使っている1

meow (@meow_noisy) on Speaker Deck

Speaker Deckを使っていて不便な点はいくつかあるが、その中の一つに1日毎のPVが記録されていない点がある。これがないために、どういうタイミングで何のスライドがどれだけ閲覧されているのかを分析できない。

したがって、1日でPVがどれだけ溜まったかを可視化するためにSpeaker Deck用のPVログ収集 & 分析する仕組みをコストを抑えて構築することにした。

アーキテクチャ

speakerdeckからPVを取得して可視化するまでの仕組みの全体像を先に示す。

f:id:meow_memow:20210915195039j:plain

区分けとしては下記の部分に分けられる

  • Speaker DeckのページからPVを取得しファイルに保存する部分
  • スクレイピングAWS Lambdaで行い、S3へ格納する部分
  • PVを記録したファイルからレコード単位に変換しDBに格納する部分
  • DBからレコードを取得し、PVをグラフとして可視化する

実装したコードは下記のリポジトリに配置している。

github.com

以下ではそれぞれの技術要素を説明する。

PVのスクレイピング部分

f:id:meow_memow:20210915195820j:plain

Speaker Deckにはスライドに関するメトリクスを取れるようなWebAPIもなければメトリクスのファイルダウンロード機能も提供されていない。したがって、ユーザがアップロードしたスライドページのスクレイピングを行い各スライドのPVを収集した。

ローカルでスクレイピングを実行するコードはリポジトリscrape_speakerdeck_pv.py に該当する。PVを取得したいSpeaker DeckのURLをこのスクリプトに書いた上で、$ python scrape_speakerdeck_pv.py を実行すると、result ディレクトリを作成し、その中にスクレイピング結果のtsvファイルを書き込む。

取得対象は、スライドのユニークID(Speaker Deck側が勝手に付与), スライドのタイトル名、PVである。

これを、次のようなフォーマットでtsvファイルで保存する。ファイル名は取得した日付にしている。

たとえば、2021-08-05.tsvは下記のような内容である。

ad7b2a026bd74e00a2c3915043c7eecb  Security Days Spring 2021のOSINTに関する講演4つの簡易まとめ/os_int_webinars2021spr    438
b9cb90f9e11845c9afe619a61c64798b    初心者向けOSINT CTF! Cyber Detective CTFに挑んだ!/cyber_detective_ctf  291
891f7fb535e14a54a85ee8792f734063    画面共有時にセンシティブな情報を意図せず公開してしまう事例の調査/sharing_screen_and_confidential_data   195
...

もちろん過去のPVは取得できないので、スクレイピングを開始した日からのロギング開始となることに注意されたい。

FaaSによるスクレイピングの定期実行

上記のスクレイピングをかつては手動のトリガーでやっていたがいい加減面倒だったので自動化した。

毎日1回定期実行といえばcrontabが第一に考えられるが、我が家には常時起動しているマシンはないためランニングコストを考慮してFaaSであるAWS Lambdaを使った。tsvのファイルの格納先はS3としている。

まず、LambdaにはS3を参照できるように権限を付与する。

次に実行するPythonの関数を登録する。Lambdaで実行する関数を含むスクリプトリポジトリlambda.py に該当する。これをコピーしてLambdaの関数として登録する。

スクレイピングに使用するrequestsBeautifulSoupはLambdaのPythonランタイムにはインストールされていないので、レイヤーを作成する。レイヤーの作成の仕方はAWS公式サイトの手順を参考にした。レイヤー作成時に気をつける点としては、python/lib/python3.8/site-package という構造でパッケージを格納する必要があることである。

この関数を1日1回定期実行するように設定する。すると、S3の指定バケットにtsvが格納される。

あとは、そのバケット$ aws s3 sync でローカルと同期すればよい。

Lambdaのエラー通知

Lambdaでエラーが起きた時にSlackに通知が飛ぶようにした。アラートの流れとしては、Lambda→CloudWatchAlarm→SNS Topic→AWS Chatbot→Slackである。これは下記のブログ記事を参考にさせていただき実装した。記事の通りSlackへのアラートをとても簡単に構築できた。

masakimisawa.com

tsvファイルをレコード単位に整形しDBへ挿入

tsvは日付ごとの各スライドのPVが記載されているが、これを各スライドごとの日付ごとのPVに変換してDBに格納する。

これらの作業はAWS GlueとRDSを使えば完全にAWS上で運用することができるが、ランニングコストがばかにならないのでローカルの方にDBを構える。

DBには手軽さを考えてSQLiteを使用した。

DBのER図は下図の通りである。slideテーブルでスライドを、slide_pb_dateテーブルでその日のスライドのPVを保持する。

f:id:meow_memow:20210915200740p:plain

このスキーマに沿うようにtsvファイルの各レコードを整形し、DBに挿入する。

この処理はリポジトリinsert_records_into_sqliteDB.py に該当する。

$ python insert_records_into_sqliteDB.py を実行すると、DBにレコードを挿入してくれる。

PlotlyでPVを可視化

この時点で、DBにはスライドに対して日付ごとのレコードが入っている。これをグラフでプロットする。インタラクティブにグラフを操作したかったため、Jupyter Notebookを立ち上げて、Plotlyで可視化する。

これはリポジトリvisualize.ipynb をJupyter Notebookを立ち上げればよい。

あとは、最後までコードを実行すると、下図のように各スライドの日付ごとのPVが可視化される。また、UIを操作すれば、スライドごとのPVを比較することもできる。

f:id:meow_memow:20210915201827p:plain

おわりに

Speaker DeckのPVをロギングし、可視化する仕組みをなるべくコストを抑えて実現する方法について説明した。
自分で溜まったログを可視化した感想としては非常に役立っている。急上昇しているスライドがあった時、何が原因でそうなったのかを調べるのが楽しい。また、今日はPVが増えていないと思っていてもグラフで見ると1つ増えているのがわかったりする。
このようにグラフを確認することで、次の発表へのモチベーションにしている。


スポンサードリンク


  1. Slide ShareではなくSpeaker Deckを使っている理由はこちらの記事で述べている。