はじめに
IT企業のインターン情報が多数掲載されている魔法のスプレッドシートですが、新しい募集が掲載されても変更の通知を受け取ったり差分を確認する機能がないため、優秀な人材と企業がマッチする機会が損なわれるという課題を感じていました。
そこで今回は、魔法のスプレッドシートが更新されたときにSlackに通知を流すSlackAppを作ってみました。
完成したコードはGithubで公開しているので参考程度にどうぞ。
SlackAppはこちらのサイトから追加できるようにしました。
技術選定
- 魔法のスプレッドシートのサイト(Notion)を取得
- 自身の管理するDB上に保存されている求人一覧を取得
- 1と2の差分を計算
- DB上に保存している求人の追加・削除
- 追加された求人についてSlackに通知
技術として検討する必要があるのは、
- SlackApp本体(一番左の技術)を何の言語で書くか
- DB(左から3番目)を何で作るか
の2点になります。
1の候補としては、Go, Typescript, Rubyの3つを、
2の候補としてはPostgres, AWSのDynamodb, FirebaseのFirestoreの3つ
を考えました。
1についてはNotionからデータを取得できるかという点で大きな制約がありました。
魔法のスプレッドシートは2023年からNotionのwikiで公開されるようになりました。NotionのAPIは、自身が管理するノート以外は(たとえ公開されているノートでも)APIで取得することが出来ないので、スクレイピングでデータを集めることにしました。
しかし、Notionのwikiは動的にレンダリングされているため、単純にHTTP GETでデータを集めることは出来ません。そこで、今まで何度かお世話になったことがあるseleniumを使うことにしました。
seleniumはchromeのようなブラウザの動きを模倣してくれるライブラリで、複数の言語でサポートされているという強みがあります。seleniumは今までのプロジェクトでも使ったことがあり、軽く調べた限りではweb driverを使ったスクレイピングにはseleniumがよく使われていそうだったので今回もseleniumを使うことにしました。
seleniumは色々な言語でライブラリが存在しており、Go, Node, Rubyでも対応するライブラリが見つかりました。ただ、Goのselenium clientは3年以上メンテナンスされていなかったため、この時点でGoは却下されました。
また、javascriptのseleniumについては、ドキュメントが存在しなかった(自動生成が壊れているらしい https://github.com/SeleniumHQ/selenium?tab=readme-ov-file#documenting )ため、消去法でRubyを採用することにしました。
2については、まず「今回どんなデータを保存する必要があるか」から考えました。今回保存する必要があるのは、求人データのリストに過ぎず、JSONレベルの記述力があれば事足ります。そのためにSQLのER図を考えてcreate tableして・・・というのは面倒だったため、今回はNoSQL系のサービスを使うことにしました。
次にDynamoDBを軽く触ってみたのですが、DynamoDBはデータが整数の場合{ N: "3" }
、文字列の場合{ S: "foo" }
というような形で返されるため、いちいちMarshalとUnmarshalをしないという手間がありました(RubyのSDKだと必要ないっぽい?)。これは面倒だと感じ、DynamoDBはやめてFirestoreを使うことにしました。
Firestoreは無料枠が読み込み・書き込みの回数で決められていて、個人開発の範囲で無料枠を超えることはまずありえないため他プロジェクトでも重宝しています。
実装
実装の中で最も難しかったのはスクレイピングの部分です。基本的にクラス名を用いてエレメントを取得していくのですが、来年の「魔法のスプレッドシート2024」が出たときにできるだけ変更箇所が少なくなるようにしたいという思いがありました。
そのため、BlockIDなどのクラス名で指定するのではなく、.notion-table-view-row
のような普遍的なクラス名を駆使してエレメントを指定するように意識しました。
また、来年の魔法のスプレッドシートで万が一Notionが使われなくなったとしても影響が最小限に抑えられるよう、コードを適切に分割し、スクレイピングの処理が他のコードに影響しないよう意識しました。
SlackAppの公開
SlackAppを公開するにはOAuth2認証をできるようにする必要があります。そのためGo言語で簡易的なサーバーを立てて認証ができるようにしました。これについては別記事で解説しようと思います。
最後に
今回はSlackAppを作成して魔法のスプレッドシートの更新通知がSlackへ流れるようにしました。
しかしまだテストコードが全然書けていないので、これからテストをごりごり書いていこうと思っています。