見出し画像

ツール作成日記 vol.2 - 非同期処理

ツールのバージョンアップしました。バージョン3.0

画像1

https://licodeenar.github.io/notelist/

前回の記事」で、いずれ追加するかもといってた機能。なんだかんだで機能追加しちゃいました。やりはじめると、ついつい止まらなくなってしまいます。

1|JSONでデータを返す
2|なまえを一緒に表示する
3|パフォーマンス向上(非同期処理化)
   →フォロー2,000人まで取ってこれるようにしました

1|JSONでデータを返す

JSON形式でデータ表示する機能を追加しています。

ボタン下のチェックボックスにチェックを入れると、データがJSON形式でそのまま表示されるようになります。これをコピーして、ローカルのテキストファイルなどに貼り付ければ、みなさんが作ったツールなどにデータインポートができるんじゃないかと思います。

画像2

JSON(読み:じぇいそん)っていうのはデータ構造の形式のことですが、これで保存できることで、いろんな活用の幅が広がるかと思います。リストを一覧表にすると見やすくはなりますが、Excelに取り込んだり別の用途でデータを役立てることが難しくなります。そこで見た目は悪いですが装飾せずに素のデータを取得できると情報そのものの利便性はあがります。

もともとの作りが、Google Apps Scriptで取ってきたおともだちのリストをJSONで受け渡すようになっているので、今回は逆にこの中身を何も加工せず表示するようにしました。なので機能追加といっても、むしろ何もしてなくってチェックボックスを追加したくらい。。。

画像3

おまけ体験コーナー:
https://tableconvert.com/ ← このサイト知ってます?知ってるとたまに便利です。こういったツールを使えばJSON形式のデータをその他のファイル形式(CSVやExcelなど…)に簡単に変換することができます。

メニュー:Import → JSONタブを選択 → JSON形式のデータをコピペする → 「Import Data」ボタンをクリックする → データの取り込み完了!

2|なまえを一緒に表示する

IDだけを表示していたのですが、やっぱり視認性が悪いので、なまえ表示機能を追加しました。IDの下に小さな文字でなまえを表示しています。

画像4

IDのときと同様Parserライブラリを使って取ってくるだけなので、やってることは難しくはないのですが、、、テストしてみるとうまく行かない。

noteって実は内部的に数種類のアカウントがあるようで、独自ドメインを持っているユーザだったり、その中でも特別な処理をしているユーザが稀にいたりするみたい。その種別によって内部的な作りが異なっていてデータを取ってこようとするとパターンに収まらないケースが存在します。

わかってしまえば、、、なんですが、そのデータが何なのか探し出すのに手間取りました。

もしかして、他にもそういった例外ユーザがいたりすると、うまく名前が表示されないかも!?

3|パフォーマンス向上(非同期処理化)

ジャジャーン!今回の改修のメイン機能です!!

見た目は全く変わっていないのですが、パフォーマンスがかなり改善されたと思います。

前回までは、UrlFetchApp.fetch( url ); という関数を使って、フォロー/フォロワーのページを1ページずつ「順番に読み込む」方法をとっていました。

let noteURL = "https://xxxxxx?page=";

for(let page = 1; page <= max; page++){
    // 1ページずつページを読み込み
    let html = UrlFetchApp.fetch(noteURL + page).getContentText("UTF-8");
  
    // htmlをParse...
     
}

これだと、だいたい1ページ読み込むのに、2秒弱くらいかかっていたので、20ページ(400人分)を読み込もうとすると、35から40秒くらい時間がかかっていたかと思います。

ページを「並列して読み込ませる」いい方法がないかと調べてみると、非同期で複数処理を実行できる UrlFetchApp.fetchAll( url[] ); という関数があることを知りました。これをうまく使って複数ページをいっぺんに処理することができれば、かなりのパフォーマンスの改善が見込めそうです!

const FETCH_MAX = 10; //一度に読み込むページ数
let noteURL = "https://xxxxxx?page=";

for(let fetchCnt = 0; fetchCnt < max; fetchCnt++){
    let start = fetchCnt * FETCH_MAX;
    let end = start + FETCH_MAX;
    let requests = [];
    for(let page = start + 1; page <= end; page++){
        requests.push(noteURL + page);
    }

    // Nページ毎にまとめてフェッチ
    let htmls = UrlFetchApp.fetchAll(requests);
  
    // 取得したページをすべてマージ
    let htmlAll = '';
    for(let i = 0; i < FETCH_MAX; i++){
        htmlAll += htmls[i].getContentText("UTF-8");
    }

    // htmlALLをParse...

}

使い方は簡単で、基本的にはurlの指定部分が配列になっているだけです。この配列に複数のURLを指定すると、結果も配列で戻ってきます。

10個URLを指定すれば同時に10ページを読み込んでくれます。こうすれは10ページ読み込むのに2秒 x 10 = 約20秒 かかっていたのが、同時に処理されるのでロスがなければ2秒そこそこで取ってこれるようになるはずです。

こういうところが、プログラムの面白いところですね。プログラム自体のコード行数は増えているのに処理時間は短くなったりします。データって目に見えませんが、プログラムの実装方法によって応答に変化がみられると、それはどこかの実態をもったサーバで処理が物理的になされた結果なのだと実感することができますね。

ちなみに今回は同時に処理するページ数は10ページに抑えています。おともだちが多いときは一度に取得する数を増やしたほうが速くはなりますが、おともだちが少ないとそれだけ空振りするリクエストが多くなってしまうので、できるだけサーバーに無駄な負荷をかけないように控え目にしています。それでも、1回の処理で10ページ(200人分)いっぺんに取ってこれるので、MAXで400人のフォロワーを取得するのにも、2秒 x 2回 = 4秒程度で応答が帰ってくるはずです。古いバージョンでは3、40秒かかっていたので、パフォーマンスはかなり改善されたと思います。

パフォーマンスが改善されたので、フォロー「している」人を取ってくる上限を2,000人まで増やしてみました。2,000人フルに取得しても20秒程度で応答が帰ってくるのではないかと思います。この感じだと最大値をもっと上げてもいいような気もしますが、そもそも2,000人以上もフォローしているユーザそんなにいないと思うので、いったん上限は2,000人までにしておこうと思います。

フォロー「されている」人の取得上限は、noteの仕様上400人までしか画面表示できない制約があるので、これ以上増やすことは残念ながらできません。。。

ここに今回の fetchAll() のソースコードを置いておきます。旧バージョンのfetch() を使った関数も一緒に残してあります。参考までhttps://github.com/licodeenar/notelist_webapi/blob/main/get_note_lists.gs




この記事が気に入ったらサポートをしてみませんか?