【個人開発】「Qiitaでいいねしたら草生えるページ」をリニューアルして表示時間を数十秒→1秒に改善した

半年ほど前に作った「Qiitaでいいねしたら草生えるページ」のサイトをリニューアルしました。
今ままでいいねのカレンダーヒートマップが表示されるまで数十秒以上かかっていたのが、１秒程度で表示できるようになりました。

- 以前の紹介記事
    - [Qiita で いいね したら草生えるページ(β版)を作った話](https://qiita.com/tag1216/items/55f5ab609db590735be1)
- 作ったもの
    - [Qiiner]
    - [Qiitaでいいねしたら草生えるページ]

[Qiiner]:https://qiiner.tag1216.net/
[Qiitaでいいねしたら草生えるページ]:https://qiiner.tag1216.net/likes-heatmap




# 「[Qiiner]」 とは

https://qiiner.tag1216.net/

Qiitaから取得したいいねのデータを使用して色々とやってみるサイトです。
今のところコンテンツは「Qiitaでいいねしたら草生えるページ」のみです。




# 「[Qiitaでいいねしたら草生えるページ]」とは

https://qiiner.tag1216.net/likes-heatmap

Qiitaユーザーがいいねした日付でカレンダーヒートマップを表示するページです。

https://qiiner.tag1216.net/likes-heatmap/tag1216

<img width="600" alt="スクリーンショット 2019-06-24 20.07.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35192/1518fa44-aed0-24ba-1e61-0f0e636997fc.png">




# 変更内容

- システム構成
- いいねのデータ取得をWebページのスクレイピングからAPIを使用するようにした
    - → いいねした日時が正確にわかるようになった
- いいねのデータ取得タイミングをオンデマンドからバッチ処理に変更した
    - → 表示のパフォーマンスを劇的に改善できた
- データの保存先をFirestoreからStorageに変更した
    - → 全期間のデータを保存できるようになった
- URLパスにユーザーIDを入れた
    - → 表示結果のページをブックマークできるようになった
- 年毎のフィルターを作った
    - → 1年以上前のいいねが見れるようになった




## システム構成

Before
![Copy of Qiiner システム構成図(旧) (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35192/7bf976b6-8162-c162-360d-b755545bc2a8.png)

After
![Qiiner システム構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35192/4c598d93-7867-7137-9faa-447179d75587.png)



## いいねのデータ取得をWebページのスクレイピングからAPIを使用するようにした

以前はユーザーの [いいねした記事(https://qiita.com/:user_id/like)](https://qiita.com/tag1216/like)　のページから取得していました。
このページではいいねした日時はわからないので推定した日付を使っていました。([いいねした日の推定方法](https://qiita.com/tag1216/items/55f5ab609db590735be1#いいねした日の推定方法))

今回のアップデートではデータ取得を [Qiita API(/items/:item_id/likes)](https://qiita.com/api/v2/docs#get-apiv2itemsitem_idlikes) に変更しました。
これを使うことでいいねした正確な日時がわかるようになりました。




## いいねのデータ取得タイミングをオンデマンドからバッチ処理に変更した

以前は画面でユーザーを検索したタイミングでQiitaからデータを取得していましたが、今回のアップデートでデータ取得を予めバッチ処理で行うように変更しました。

以前はユーザーを検索してから結果が表示されるまで数十秒以上かかることもあり、くそイライラしていたユーザーも多かったと思います。
今回の変更でだいたい1秒程度で表示されるように改善しました。

なお、長時間のバッチ処理は Firebase の Functions ではできないので別途VPSを用意して実行するようにしました。
現在は1日数回の頻度でいいねデータが更新されています。




## データの保存先をFirestoreからStorageに変更した

データの保存先をFirestoreからStorageに変更しました。

Firestoreでは1データ1MBという制限があるのと、データ取得に時間がかかる為、直近1年分(または50ページ分)のデータしか保存していませんでした。
保存先をStorageに変更することでサイズ制限がなくなった為、ユーザーの全期間のいいねを保存できるようになりました。




## URLパスにユーザーIDを入れた

以前はユーザーを検索した後もURLはそのままでしたが、URLパスにユーザーIDを追加するようにしました。

https://qiiner.tag1216.net/likes-heatmap/tag1216

表示結果のページをブックマークできるようになります。




## 年毎のフィルターを作った

ユーザーの全期間のいいねを取得するようにしたので、カレンダーヒートマップの表示も年毎の表示を選択できるようにしました。




# データの取得について

「Qiitaでいいねしたら草生えるページ」でのいいねのデータ取得方法を説明します。

いいねデータを取得するバッチ処理は以下の手順で実行しています。

1. 記事一覧の取得([/api/v2/items](https://qiita.com/api/v2/docs#get-apiv2items))
2. 各記事のいいねの取得([/api/v2/items/:item_id/likes](https://qiita.com/api/v2/docs#get-apiv2itemsitem_idlikes))
3. ユーザー単位でいいねをまとめる
4. Firebase storage へアップロード

当初は全記事のいいねの取得は非常に時間がかかる為、定期的にデータを更新するのは非現実的だと思っていました。

当初の試算では、

`全記事数(約40万) / 1時間あたりAPI呼び出し回数(1000) = 400時間 = 16日`

もの時間がかかる想定でしたが、以下２点を工夫することで短縮できるようになりました。

### いいね数0の記事はいいねを取得する必要はない

`1. 記事一覧の取得(/api/v2/items)` ではいいね数を取得できるので、ここでいいね数が0の場合は `2. 各記事のいいねの取得(/api/v2/items/:item_id/likes)`を実行する必要はありません。

全記事約40万件のうち約10万件はいいね数０だった為、2. を実行する必要があるのは3/4の約30万のみでした。

### 前回取得してからいいね数が変化した場合のみいいねを取得すればいい

`1. 記事一覧の取得(/api/v2/items)`で取得したいいね数が前回と同じならばいいねは変化していないと考えられるので、`2. 各記事のいいねの取得(/api/v2/items/:item_id/likes)`を実行する必要はありません。[^1]

初回だけは数日かけて全記事のいいねを取得する必要がありますが、それ以降は1日でいいねされる記事数は約8000件ほどで、これなら1日数回の更新ができます。



# 今後の予定

以前、「[Qiitaいいな〜分析](https://qiita.com/tag1216/items/6363c3769b5d0d7cfe47)」でいいねのデータを使った分析を行いました。
元データが違うので同じものは出せませんが、以下のようなチャートを「[Qiiner]」追加していきます。

- いいね数・記事数の推移
- アクティブユーザー数・離脱ユーザー数の推移


[^1]: 実際にはいいね数が変化していなくても「あるユーザーがいいねを解除してから別のユーザーがいいねした」というケースもあるので、。厳密にはいいねが変化している可能性があります。