meowの覚え書き

write to think, create to understand

ReconVillage CTFの過去問まとめ

まとめがなぜかなかったので自分でまとめた。

まとめる単位は「DEFCONのナンバリング(開催年) 現地orオンライン」

ChromeのGoogleレンズの画像検索結果を他国準拠にする

(画像 左:日本準拠のGoogleレンズの結果。 右:アメリカ準拠のGoogleレンズの結果)

CTFのOSINT問を解く上で必須のツールであるGoogleレンズですが、私の環境だと日本のページを優先的に提示していたため、どのようにその傾向を変えるのか調べました。

結論としてはChromeのプロファイルにGoogleアカウントを紐付け、②そのGoogleアカウントの言語設定を変えるだけでよいです。

背景

6月にCapture The Talent と K3N4D4M5氏が共催したOSINT CTFがありました。

そのCTFの1問目は「↓の画像が撮影された公園の名前を答えよ」という、オーソドックスな問題でした。

画像の撮影場所の特定をする問題のセオリーは、画像ファイルのEXIFチェックと各種検索エンジンによる「画像で検索」にかけることなのですが、いずれも通用しませんでした。
私の過去のOSINT CTFの参加経験から考えて、1問目はセオリーに則れば解ける問題が用意されていることが多かったですし、この問題の回答者数がみるみる増えていくので、自分の調査手順に問題があると考えました。

私の調査の中で気になったのは、ChromeGoogleレンズの検索結果が日本のページばかりであることでした。このCTFは主催者は海外の方なので、この画像は日本国外で撮影された可能性が極めて高く、日本語のページでは扱われていないと考えました。

そこで、これをアメリカのページを優先的に出力するようにできるか試しました。

最初はVPNを介したり、Chromeのセンサで所在地を変えたりしてみたのですが、日本の検索結果が優先して返されるままでした。最終的にたどり着いた方法が冒頭で述べた、Googleアカウントの言語設定を変えることでした。
以下ではその方法について説明します。

言語設定の変更手順

(手順は2022/08/06時点のChromeのUIです。)

まず、ChromeのプロファイルにGoogleアカウントを紐付けておきます。

Googleアカウントを紐付けたプロファイルでChromeを開きアカウントアイコンをクリックし、「Googleアカウントの管理」をクリックします。

Google アカウント管理画面の検索フォームで「言語」と入力し、「言語」の設定ページを開きます。

「言語」の設定ページにおいて、「+ 他の言語を追加」ボタンをクリックします。

Googleレンズで検索結果を優先したい言語・国を選択します。ここでは 言語を「English」、国を「United States」としました。

現在は日本語が最優先になっているので、「↑」ボタンをクリックし、「English」を日本語より上に持ってきます。

すると、言語が変わりました。

この状態でGoogleレンズを使用します。すると、先程と同じ画像を入力したにもかかわらず、英語のページが優先的に表示されていることがわかります。

最初に紹介したOSINT問では、Googleレンズがアメリカ準拠の状態で検索範囲を車付近にフォーカスをすればフラグのページがヒットしました。

まとめ

ChromeGoogleレンズが返す結果を変える方法を説明しました。
日常でGoogleレンズを使う時に海外の結果が出るのは不便かもしれないので、OSINT CTF用のGoogleアカウントとChromeプロファイルを用意しておき、CTF中のみ、そのChromeプロファイルでGoogleレンズを使用するのがよいと思います。

地理的位置に基づく店舗名の(エセ)AND検索

画像引用元: https://tabelog.com/hiroshima/A3401/A340121/34000922/dtlphotolst/3/smp2/

この記事では、↑の写真の撮影場所の特定に挑戦した際に考案した手法について記す。具体的には、店舗名2つを指定すると距離の近い店舗ペアを列挙する手法を実装し、スターバックスコーヒーとFrancFrancが隣接する建物の特定を試みた。

先に述べておくと紹介する手法では場所の特定はできなかった。しかし、今後撮影場所の特定を行う際に流用できる可能性がある。


スポンサードリンク

続きを読む

音声認識エンジン『Julius』を使って韻を踏んだフレーズペアを探す

f:id:meow_memow:20211231223546j:plain

こちらは『創作+機械学習 Advent Calendar 20212枚目の2日目の記事です。

adventar.org

先に記事の要旨を説明すると、音声認識器の認識結果を敢えて複数個出力することで韻の近い単語1列(以下フレーズと呼ぶ)を検索するツールとして使えないか検証したという内容です。簡易的に実験した結果、狙ってフレーズを検索するのは難しいですが、面白いフレーズペアを偶発的に発見する用途には使えそうという印象です。

背景

作詞の現場で、押韻を用いた表現を探したい(韻を踏みたい)ケースがあります。例えば「思い出」の母音「o o i e」に近い言葉を探すと「遠い目」という単語が使えそうです。

語彙力が豊富な方やラッパーをなりわいとしている方であれば瞬時に言葉の候補が思い浮かぶのかもしれませんが、経験・スキル・センスが問われる作業だと思います。

そこで誰でも再現できる手順で、似たような音の響きを持つ言葉を探す方法を考えます。

既存手法と課題

考えうる方法としては2つあります。

既存手法1: 過去事例の流用

既存手法の1つめは過去事例の流用です。これはラップの歌詞やMCバトルの書き起こしから使えそうなものをストックしておくというものです。
過去事例の情報源ですが、例えば『韻ノート』には、ラッパーの踏んだ韻を含むラインが第三者により登録されていることがあります。

in-note.com

例えば”R-指定”というラッパーだと次のようなものがあります。

「しかと見ときな格の違い キッズとキングの箔の違い お前殺すぜ覚悟しな ここに転がるカスの死体

https://in-note.com/rhyme_groups/12

これならば、プロが踏んだ質の良い韻を使うことができそうです。
しかし、この方法で自分の作品を作りたいと思う人はいないと思います。質が良いということはオリジナリティも高く、やみくもに使うと”他人のパクリ”とみなされるためです。作詞作業に限らず創作界隈においてはご法度でしょう。
したがって、他人が踏んでないような韻を探す必要があります。

既存手法2: 母音に基づく検索

既存手法の2つめは母音に基づく検索方法です。
インターネット上には母音で引ける辞書サービスがあります。

kujirahand.com

たとえば「思い出」と踏みたい韻を探すために、母音“おおいえ”で検索すると、「乙姫」、「とこしえ」というものがヒットします。 また、「五ノ池」のような固有名詞もヒットします。「おおいえ」を母音とする単語単体だけでなく、さらに「おおいえ」を前後に含む単語を検索することができます。

また、"背景"の項目で紹介した「韻ノート」ならば、母音に直さずとも、韻を踏みたい単語で探せます。
試しに「思い出」で検索すると、「暴行事件」「東京事変」などという結果が提示されます。「っ(促音)」や「ー(長音)」を考慮した検索ができるので、現実の発声に即した検索ができます。

既存手法の課題

上に挙げたツールは作詞作業には十分使える一方で限界も2つあると考えます。
1つ目は基本的に1単語対フレーズ(もしくはフレーズ対1単語)でしかヒットしないことです。フレーズ対フレーズではどのようなものがあるかといえば例えば、 『夕方ノスタルジー / ZORN Feat. WEEDY』では「ラムネ の 瓶」と「鳴る メロディ」という韻を踏んでいます。3単語対2単語ですし、単語の切れ目も一致していないので先程の検索ツールではヒットしないでしょう。
2つ目はツールは母音は完全一致のみヒットすることです。 途中に1文字程度の母音が多めに入っていたり抜けていたりというものはヒットしないです。前述の『夕方ノスタルジー / ZORN Feat. WEEDY』では「賃貸マンション」と「心配無いよ」で踏んでいる箇所があります。母音こそ完全一致ではないですが、歌詞の文脈とも合わさって全く違和感がありません。

以上から、既存手法には課題は3点あると考えます。

  • オリジナリティが発生しない程度に一般的かつ新規性がありそうなものを探すこと
  • フレーズ同士の組み合わせを探すこと
  • 音が完全一致でないものも探すこと

提案手法

この課題3つを音声認識エンジンJuliusを使うことで同時に解決できないかと考えました。

julius.osdn.jp

核となるアイディアは「Juliusに実装されている、似たような音の響きを持つ単語の候補を時間区間ごとに出す機能を使う」ことです。具体的には下図の"単語グラフ"のようなイメージです。

f:id:meow_memow:20211231224528j:plain

図: 公式ページの"単語グラフ出力"より引用

このような単語の候補のグラフから、韻を踏んだフレーズペアを探すというものです。

Juliusの音声認識アルゴリズム

Juliusを使うとなぜアイディアが実現できるのか、Juliusで採用されている音声認識アルゴリズムの大まかな仕組みを知っていた方が理解しやすいと思うので説明します。(とはいえ他人に説明できるほど音声認識を理解できていないのでやんわりとですが...)

ここで覚えておいていただきたいことはJuliusの音声認識は内部で音響(何と発音されているか)と言語らしさ(言葉としての現れやすさ)をスコア付けするグラフ探索問題を解いているということです。

f:id:meow_memow:20211231224734p:plain

図: Juliusの論文より引用

  1. 特徴抽出: 入力されたディジタル音声信号から一定時間長で識別のための特徴量を連続的に抽出していきます。JuliusではMFCCという特徴量を抽出します。
  2. 音素系列の認識: 処理1で抽出した特徴量系列を音響モデル(図で音素モデルと呼んでいるもの)により、音の並びである音素2列を推定します。具体的な処理としては、音素ごとにHMMが用意されており、観測された特徴ベクトルを生成する尤度を各HMMが計算しスコアを算出します。
  3. 音素列から単語に直す: 音素列は発音辞書を用いて音素列に該当する単語が存在するかをチェックします。
    1. 発音辞書は次のようなものです: https://github.com/julius-speech/dictation-kit/blob/master/model/lang_m/bccwj.60k.htkdic
    2. もちろん「a sh i t a」を「明日」とするのか「足 田」とするかという複数の変換先がありますが、そこは手順4で確率的に決めます。
  4. 単語列の言語的確率を付与: ここまでで、音素列が単語列へと変換されていますので、言語モデルを用いて単語列に言語的観点から単語列の生成確率(スコア)を与えます。例えば「今日 の 天気」と「京都 天気」だと前者の方が確率が高く与えられます。
  5. 手順2〜4は、どのHMMから通ってきたか、どういう単語に直したかなどで探索経路が異なるため、複数のパスを候補として持ちながら探索を進めます。
  6. パスの候補の中から一番音響モデルが出したスコアと言語モデルが出したスコアの高いものを音声認識結果として提示します。

Juliusでは2パス探索を採用しており、手順2〜4を2回行います。1パスめでは始点から終点へ順方向に計算処理の軽いモデルでスコアを求めます。2パスめではより前後の文脈に応じたモデルで終点から始点へ逆方向に探索します。このとき1パス目の情報(どの時刻にどういう単語がどういうスコアであったか)をヒューリスティックとして用いることで高速に探索します。

アイディアの再掲

Juliusは音の近さと言語らしさを考慮してグラフの探索問題を解いているという点を説明しました。また、複数の経路を探索候補として保持していることや、いつの時刻に何の単語がありそうかという内部表現を保持している旨を述べました。

ということは、自分が検索したい韻を踏んだフレーズをJuliusに認識させれば、複数の認識結果やJuliusの内部表現から、音の聞こえ方が似ていて日本語としても自然な表現を集めることができるのではないかと思いました。

このアイディアを実現する方法には至ってシンプルで「Juliusの内部表現を出力するオプション3をつけて、踏みたい韻を含むフレーズを発声し、表示された結果をメモする」ことです。

Juliusで実際にフレーズペアを探す

Juliusのセットアップ

大変ありがたいことに、Juliusをすぐに試験できるように”ディクテーションキット”が公式的に用意されています。

julius.osdn.jp

”ディクテーションキット”には各OSごとのJuliusのバイナリと、日本語音声認識のためのリソース(学習済みの音響モデルと言語モデルなど)が既に同梱されており、コマンドラインからバッチを実行すればすぐに音声認識を開始することができます。

音声入力手段はデフォルトではマイク入力ですが、実行オプション(-input file)で音声データ4を指定することも可能です。

実験

認識結果上位を表示するように出力

Windows10でrun-win-gmm.batをオプションをつけながら実行してみました。

まず、自分が今踏みたい韻のフレーズを含んだセリフを音声認識し、認識のスコアの上位10つを出力してみます。 ここでは試しに「いい 思い出」に近い聞こえ方のフレーズを探してみます。

「あれはいい思い出だった」と発話した結果を記します5

sentence1:  あれ は いい 思い出 で 、 だ 。
sentence2:  あれ は いい 思い出 って 、 だ 。
sentence3:  あれ は 入り 思い出 って 、 だ 。
sentence4:  、 あれ は いい 思い出 で 、 だ 。
sentence5:  あれ は 入 思い出 で 、 だ 。
sentence6:  、 あれ は いい 思い出 って 、 だ 。
sentence7:  R Y いい 思い出 で 、 だ 。
sentence8:  あ 、 あれ は いい 思い出 で 、 だ 。
sentence9:  R Y いい 思い出 って 、 だ 。
sentence10:  あ 、 あれ は いい 思い出 って 、 だ 。

本来見つけたかった「いい 思い出」の部分はどの結果でも正しく認識されており、フレーズペアを見つけることに失敗しました。敢えて言うなら「あれ は いい」と「R Y」がフレーズ対フレーズのペアなのですが、歌詞として使えるケースは極めて限定的でしょう。

”単語ラティス形式”で出力

複数の認識結果を見ることは目が疲れるので、”単語ラティス形式”で出力(オプション-latticeをつけて実行)をしてみました。こちらは、どの区間にどのような単語がありそうかを可視化できるものです。

先程と同じく「あれはいい思い出だった」と発話した結果を記します。

-------------------------- begin wordgraph show -------------------------
   0:
   0:|-|
   1:
   0:|------|
   2:
   0:|-------|
   3:   ああ
   7:  ||
   4:   あ
   7:  ||
   5:   あー
   7:  ||
   6:   、
   7:  |-----|
   7:     、
  10:    |---|
   8:         タレ
  20:        |--------|
   9:         たれ
  20:        |--------|
  10:         多衣
  20:        |--------|
  11:         佐江
  20:        |--------|
  12:         幸い
  20:        |-----------------|
  13:         は
  21:        |---|
  14:         彼
  21:        |-------|
  15:         あれ
  21:        |--------|
  16:         彼
  21:        |--------|
  17:         かれ
  21:        |--------|
  18:         カレー
  21:        |--------|
  19:         我
  21:        |--------|
  20:         われ
  21:        |--------|
  21:         姉
  21:        |--------|
  22:         吾
  21:        |--------|
  23:         金
  21:        |--------|
  24:         腫れ
  21:        |--------|
  25:         あれ
  21:        |--------|
  26:         晴
  21:        |--------|
  27:         綾
  21:        |---------|
  28:         彩
  21:        |---------|
  29:         亜矢
  21:        |---------|
  30:         アレン
  21:        |----------|
  31:         カレン
  21:        |----------|
  32:         亜鉛
  21:        |----------|
  33:         か弱い
  21:        |----------------------|
  34:             良い
  32:            |------------------|
  35:                  の
  43:                 |------|
  36:                  は
  44:                 |------|
  37:                   は
  47:                  |-----|
  38:                    は
  49:                   |----|
  39:                         いい
  62:                        |------|
  40:                           いい
  66:                          |----|
  41:                                思い出
  79:                               |----------------|
  42:                                                 だっ
 122:                                                |-----------|
  43:                                                             た
 151:                                                            |-----|
  44:                                                                    。
 168:                                                                   |-|
-------------------------- end wordgraph show ---------------------------

やはり「いい 思い出」の部分はまとめられてしまっています。しかし、「あれ は いい」の部分に「か弱い」という表現が出てきました。母音が完全一致していないですが似たような響きの単語が偶発的に得られたと思います。

考察

自分が見つけたい韻のフレーズをピンポイントでみつけることは難しそうです。それは尤もなことで、認識結果の上位に来るものは当然似た認識結果になりやすいです。もっとスコア付けをオプションで調整し、「聞き間違え」を意図的に誘発して認識結果の多様性を上げる必要がありそうです。

このような理由から、フレーズの検索用途というよりも、何らかの音声を”単語ラティス形式”形式で垂れ流しておき、作詞に使えそうなフレーズペアを偶発的に発見してそれをストックする用途の方が適しているのではないかと思います。

私の手法のその他の限界としてはJuliusの学習モデルの制約が考えられます。例えば語彙は bccwj.60k.htkdic 内の単語に制限されています。このファイルに単語を追加することもできますが、言語モデルには含まれていないため、未知語の確率が割り振られ認識結果に出る確率が下がり認識結果としてヒットしにくいです。また、音素の連節(トライフォン)もlogicalTriというファイルで定義されており、このファイルに載っていない音素列を含む単語は登録することができません。このように、キットを改造するには音声認識への理解やエンジニアリングが必要で高い技術力が要求されそうです。

まとめ

音声認識エンジンJuliusを用いて、フレーズ同士の韻を踏んだ表現を検索(あるいは収集)する方法について提案しました。

探したい韻のフレーズを検索する用途に用いるのは必ずしも成功しないですが、実際に作詞に使えそうなネタを偶発的に発見する用途としては使えそうであることがわかりました。もちろん作詞活動だけでなく漫才の聞き間違えネタに使えそうで、コメディと相性がよさそうです6

今回は音声認識モデルでしたが、他のタスクも同様に認識結果のうち次候補以降や推論ミスも面白い用途に使えるかもしれません。

それでは、最後まで記事を読んでいただきありがとうございました。

追記:
再掲になりますが、『創作+機械学習 Advent Calendar 2021』では、創作活動用途に機械学習技術を適用する取り組みを有志が多数公開してくださっています。どの記事も面白いので、ぜひラインナップを一読いただければと思います。

adventar.org

参考文献


  1. ここでは形態素を想定しています。

  2. 音声言語を構成する最小単位です。

  3. この中間結果出力機能があることが、数ある音声認識ライブラリの中からJuliusを選んだ理由です。

  4. サンプリングレートは16kである必要があります。

  5. airpodsのマイクで入力しているため、認識結果は悪いです。

  6. 機動戦士ガンダム 閃光のハサウェイ』冒頭事前公開でも音声認識誤りが発生し低俗な字幕が表示されたために、話題になりました。

【VSCode】Pythonコードのデバッグ中に画像の入った変数を可視化できる拡張機能を試した

はじめに

この記事では、Visual Studio Code(以下VSCode)の拡張機能の1つを使った際の所感を紹介する。

私は普段、画像系の機械学習タスクに取り組む時VSCodeで開発している。 その際、画像に対して自前実装の前処理やデータオーグメンテーションがちゃんと施されているかを簡易的に確認したい場合がある。

そういう時に私はこれまでディスクに書き出して結果を確認していた1:。 しかし、もう少しスマートな方法がないかと思い、デバッグ中に画像を可視化できるVSCode拡張機能が無いか探したところ、下記の拡張機能を発見した。

github.com

VSCodeでこの名前で拡張機能を探せばヒットする。かなり長い名前なので、以下でもこのsimply-view-image-for-python-debuggingのことを「拡張機能」と呼ぶ。


スポンサードリンク

拡張機能の使用方法

公式リポジトリのgifアニメをそのまま引用するが、これを見れば使い方は一目瞭然である。

  1. 実行したいPythonスクリプトを用意する。
  2. ブレークポイントを張る。(当たり前かもしれないが)ブレークポイントの位置は、画像を可視化したいオブジェクトを変数に格納した以降の行に張ることに注意。
  3. VSCodeでデバックモードを実行し、処理がブレークポイントまで達したら、
    1. 画像のオブジェクトが格納されている変数をクリックすると、もしもその変数が拡張機能で可視化をサポートしているオブジェクトならば、電球マークが現れる。
    2. 電球マークをクリックすると、View Imageというポップアップが出る。
    3. ポップアップをクリックすると、別ウィンドウで可視化された結果が表示される。

拡張機能内部で行われている処理はとてもシンプルで、 /tmp 領域にsvifpd2いうディレクトリを作り、画像をそこに書き出して、その画像を別ウインドウで表示するというものである。

各ライブラリのオブジェクトの可視化結果結果

機械学習の画像系タスクで主に使用されるであろうPythonライブラリのオブジェクトを可視化した結果をスクショ付きで述べる。

各ライブラリごとの可視化結果を下表に示す。中には画像処理用途とは限らないものも含めてしまっているが、実用途を鑑みての判断であるのでご了承いただきたい。

ライブラリ名 可視化可能か 検証した際のバージョン
Pillow 7.1.2
NumPy(, Scipy) 1.19.5
opencv-python 4.1.1.26
imageio 2.4.1
Matplotlib 3.4.2
PyTorch 1.6.0(torchvisionは0.7.0)
TensorFlow 2.7.0

なお、その他の動作環境のバージョンは下記の通り。

Pillow・・・○

readmeにはサポートしているとは書いていないが、可視化可能。

NumPy(, Scipy)・・・○

画像ではなく単なる数値のテンソルに対して可視化をおこなってみたが、可能だった。 また、ScipyもNumPyを内部で利用しているので可視化可能。

opencv-python・・・△

NumPyがサポートされているので、もちろんopencv-pythonの可視化も可能。

ただし、opencv-pythonではChannelの順序がBGRなので、残念ながらきれいに表示されないところが玉にキズ。

imageio・・・○

私は使わないが、imageioを試してみた所、可視化可能だった。

Matplotlib・・・○

画像を扱うためのライブラリでは無いが、先程張ったgifアニメの通り、MatplotlibのFigure、Axisの可視化も可能。

PyTorch・・・○

readmeにはPyTorchのテンソルもサポートしていると書かれていたので試してみたが、確かに可視化された。PyTorch民としてはかなり嬉しい。

また、驚いたことにBatch次元を考慮して表示してくれている。

TensorFlow・・・✕

readmeにはサポートしているとは書いていないが念の為可視化を試みた。
しかし、電球マークが現れなかったので対応していない模様。なお、私はTensorFlowはほぼ使用経験がないので画像のテンソルを作る方法が正しいかは自信がない。

おわりに

デバッグ時に様々なライブラリのオブジェクトに対して画像の可視化を試みた。
その結果、おおよそのライブラリで可視化可能ということがわかった。
Python開発の生産性の向上が期待できそうである。

なお、今回テストに使用したコードと画像は↓のリポジトリに置いているので、興味を持たれた方は追試に使っていただければ幸いである。 github.com


スポンサードリンク


  1. なお、「Jupyter Notebookならば楽に画像可視化できる」という声があるかもしれないが、私は開発にJupyter Notebookは用いない。その理由はこちらの記事で書いている。

  2. 拡張機能の頭文字をとったものと思われる

Open xINT CTF 関連ページ 非公式まとめ

AV TokyoでTeam pinjaによって開催されるCTF 『Open xINT CTF』関連の情報をまとめています。
ファンサイトの位置づけです。

目次

お知らせ(2022/10/03)

2022年度 Open xINT CTF 開催決定!

  • AVTokyo公式ページにて、AVTokyo1日目(10月29日(土))に『Open xINT CTF』が開かれる告知がなされました! 🎉
  • 今年も オンライン開催、OSINTカテゴリのみ出題、チーム戦(3名まで)、参加費無料 とのことです。

公式情報

有志の方々による参加報告資料など

私の報告資料

その他のOpen xINT CTF関係のサイト

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を使っている理由はこちらの記事で述べている。