Spreadsheet + GASでaikoを讃える歌い出しドンクイズをつくった

こんにちは、id:yone-yamaです。 この記事はSpreadsheets/Excel Advent Calendar 2018の7日目の記事です。

昨日はid:minemuracoffeeさんの転生シリーズ第三話でした。控えめに言って神回です。

kakuyomu.jp

Spreadsheet、最高ですよね。某ネット系企業への新卒入社から数年間のExcel傾倒時代を経て、今はすっかりSpreadsheet信者です。過去にもこんな記事を書いたりして、その尊さを讃えてしきりの日々を過ごしています。

お気に入り関数はQUERYARRAYFORMULAIMPORTRANGEです。いかつい文字数の関数を使うとテンションが上がりますよね。

yone-yama.hatenadiary.jp

ところで

みなさんaikoって知ってます?

ご覧いただいてお分かりの通り、いわゆる女神なんですけど。

今日のテーマ

この女神 a.k.a. aiko大先生が世に生み出し給うた大名曲たちしめて232曲、シュッとアレをコレしてイントロドンならぬ歌い出しドンクイズにするということをやります

1. 曲目リストを取得しましょう

まずは曲リストを用意したいです。手打ちで用意するのも愛があっていいですが、Spreadsheetである必要がなくなるのでスクレイピングをします。

aiko.com

ただ残念ながら、aikoオフィシャルサイトにはスクレイピングできそうなlist型ないしtable型の曲一覧がありません。 (list=一覧。table=表。HTMLのタグによって判別します)

ついてはちょっとずっこい感じがしますが、歌詞サイト「歌net」のaiko楽曲一覧を拝借しましょう。

f:id:yone-yama:20181206210645p:plain

使うのはみんな大好きIMPORTHTML関数です。こういう使い方をするやつですね。

IMPORTHTML ("URL" , "対象となるlist or table" , "番号" )

なにはともあれsheet.newを叩いて新規スプレッドシートを作成しまして、 ついでA1にURLを入れます。

Sheet.newは最近リリースされた機能で、[docs | sheet | slide ].newをURLで叩くと新規のファイルが立ち上がるすぐれものです。便利だ。

f:id:yone-yama:20181205011353p:plain

本来であれば

IMPORTHTML("URL","table",番号)

みたいな感じでシュッとリストが取れます。が、

f:id:yone-yama:20181206211233p:plain

あれ?ページ内にtableが複数あるみたいですね。これでは一つの関数では取れなさそう。 そしてなんと2ページもある。aiko大師匠は名曲たくさんですからね。Webページなんぞには収まりきらない。致し方ない。

こういうときはQUERY関数を使います。標準的な使い方は以下。

QUERY("なんかの範囲" "Select 特定列数だけ取る Where 条件合致したやつだけ取る")

QUERY関数ちゃんの地味にすごいところは、異なる対象の配列を並べて全部取ってきてくれるところ。こんなふうに書きます。

QUERY({("なんかの範囲");("なんか別の範囲");("なんかこれまた別の範囲")})

全体を{}で囲って、範囲ごとに;で区切ってあげればあら不思議、全部の範囲をまるっと取ってきてくれます。 さらにSelect文(ここだけ取ってきてね)、Where文(この条件のときだけ取ってきてね)もしっかり効きます。

今回は1ページに6つtableが存在し、かつ各tableの行頭に見出し行が入っちゃってます。

これらをすべて範囲にして、かつ行頭が「曲名」になっている行を省きましょう。 つまり'{範囲A;範囲B}'に指定した上で、'Where<>曲名'になっていればよいわけです。

最終的にブチ込む関数はこんな感じになります。

f:id:yone-yama:20181205011522p:plain

長え。みづれえ。モバイルフレンドリーせえよ。

とまれ、これを用いて、歌netのaiko楽曲一覧ページから曲リストを取得していきます。

f:id:yone-yama:20181205135411p:plain

こうなりました。

あれ、曲名以外に「歌い出し」の欄がありますね。これは天が我に歌い出しドンをせよ、とのお告げでしょうか。でしょうかというか、そうです。

2. ドンを準備する

歌い出しドンのドン部分をつくっていきます。

まずは別シートに曲名と歌い出しの組み合わせだけを抜き出します。これもQUERY関数でシュッと。

QUERY(元シートの範囲,Select 抜き出したい列)

という感じでアレします。今回はA:Eを範囲として、E列(歌い出し) - A列(曲名)という順で抜き出したいので、QUERY(raw!A:E, "Select E,A")という記述になりました。

すると結果が以下です。

f:id:yone-yama:20181205135454p:plain

AとB列に歌い出し、曲名が出てきます。

3. ドンの出題順と選択肢をつくる

次に、元データの曲はあいうえお順に並んでいて味気ないのでランダムにします。 加えて正答と誤答が必要になるのでそれを準備します。

ここではRANDBETWEENという関数を使います。なんだかRYDEENみたいでイキフンがグッドですね。

ソリッド・ステイト・サヴァイヴァー

ソリッド・ステイト・サヴァイヴァー

  • 発売日: 2014/11/05
  • メディア: MP3 ダウンロード

RANDBETWEEN(下限値,上限値)

という、上と下を決めてその間で適当に数値を振ってくれる関数です。

RANDBETWEEN(1,COUNTA($B:$B)-1)

今回はこういう記述をしました。 下限値が1、上限値がMAXの曲数-1なのはOFFSET関数を使うための布石。後述します。 結果はこんな感じです。

f:id:yone-yama:20181205135559p:plain

4. 乱数をもとにいい感じに並べ替える

RANDBETWEENで生成した乱数は、OFFSETで特定セルの値を呼び出すのに使います。

OFFSET(開始セル,動かす行数,動かす列数,行の長さ,列の長さ)

これによって、参照セルの数値に基づいて違う位置のセルの値を返してもらえます。使い方はこんな感じ。

OFFSET(今の行 , 乱数ぶん行を動かす,,,)

※参照セルと行or列だけ指定すれば、あとの行数/列数は省略しちゃえます

先ごろのRANDBETWEENの下限値を1、上限を総行数 - 1にしたのはこれが理由で、 下限が0だと今いる行を取ってきちゃうので正答と誤答が一緒になっちゃうのでした。 かつ、上限が行数分だと一覧をはみだして空欄を返しちゃうという。

OFFSETはVLOOKUPに代表される他関数と違って「指定したスタート地点から”n個分”動かす」関数なので、ここがハマりどころです。

...という感じのアレをして、乱数分勝手にずれた歌い出し文を取りました。(D列)

f:id:yone-yama:20181205135705p:plain

そこにVLOOKUPをして、取り出した歌い出し文に正しい曲目を取ってくることも忘れずに。

大量の行をさばくときは1セルに入れるだけで配列一気に持ってきてくれるARRAYFORMULAをつけてあげましょうね。(E列)

f:id:yone-yama:20181205135822p:plain

さらに、D列と全然関係ねえ曲目も取ってきましょう。

同じく、RANDBETWEENでつくった乱数(G列)を参照してOFFSETでずらした曲目を取ってきます。(F列)

結果がこちら。

f:id:yone-yama:20181206133744p:plain

こうして、D:歌い出し / E:歌い出しに対応した曲名(正答)/ F:関係ねえ曲名(誤答)の3列=歌い出しドンのドンが取得できました。

実際のシートはこちらです。味気ない。ここからの作業で、味気をあらせていきます。

docs.google.com

5. ようやく来たぜGAS

元となるシートがやっと出来たので、ようやく歌い出しドンのドンたるゆえん、クイズ形式に取り掛かります。

ここからはさすがにSpreadsheetだけでは出来ない範囲なので、GAS(Google App Script)を使っていきます。 僕も全然詳しくないので詳細の説明は省きますが、「JavaScriptでいい感じにSpreadSheetと他サービスをアレできるなにか」と覚えておけばよさそう。

GASみたいな言語は基本的になにやるにしても先人がいるので、Qiitaでソリューションを模索します。

探したらドンピシャな先人がおられた。

qiita.com

こちらを基に、というかこのスクリプトをコピペさせてもらって、クイズを作っていく形です。

「ツール > スクリプトエディタ」にこんな感じでいれます。

f:id:yone-yama:20181206134058p:plain

Google FormのIDを入れる部分があるので、そこだけ新しいFormを作成してIDをいれます。 google系のサービスはURLの中のなんかよくわかんねえ長え文字列がたいがいIDなので、獣の直感でもってIDを記載していきます。

f:id:yone-yama:20181206134018p:plain

Form側はこんな感じです。中身は空にしときましょう。

f:id:yone-yama:20181206134251p:plain

ちなみに処理が走る前にform側の設定で「テスト」をONにします。なんかよくわかんないけどこれがONになってないとエラーを吐きます。

f:id:yone-yama:20181206134519p:plain

GetRange(範囲選択)がちょっとハマりどころです。行と列の指定順序を間違えないように。今回は233行もあるので、全部クロールすると重すぎました。

GetRange[開始行 , 開始列 , 行数 , 列数]

開始を4行目/1列目に指定して、設問数となる行数は20行にしてみます。穏当な設問数ですね。

f:id:yone-yama:20181206134422p:plain

入れました。これでRUNしましょう。保存して再生ボタンを押します。

f:id:yone-yama:20181206134937p:plain

処理、進んでいそう。処理をしている間は基本暇なので、当然aikoのニューイヤームービーを貪るように見ていきましょうね。


aiko new year CM 2016


aiko new year CM 2014

~~

おわりました。意外と早い。なぜならaikoのニューイヤームービーを貪るように見ていたから。

終わったあとにGoogle Formの対象formを見ましょう。おおっ、ちゃんと仕上がっている!!!!

f:id:yone-yama:20181206135442p:plain

名前などもよしなにつけましょう。google formは冒頭に動画を入れたりトップ画を変えられたりするので、気持ちに応じてやったりやらなかったりします。 今回のヘッダーは僕が今年の夏を捧げたLove Like Aloha Vol.6の終演後に茅ヶ崎サザンビーチにブチ上がった花火の様子です。生きててよかった。

f:id:yone-yama:20181206134851p:plain

そんなこんなで

できました。

docs.google.com

アルバム曲だけではなく、シングルカップリング曲ですらも無慈悲無作為に選定しているので難易度は鬼中の鬼です。 ぜひ回答してみて下さい。はたして満点を取れる限界オタクはいるかな!?!?(僕は満点でした)

というわけで

Excel/Spreadsheetsアドベントカレンダー7日目でした。ありがとうございました。ブログは受け取る人の気持ちを考える必要がなくて好きです。

次は@daaaaaaiさんの「メンテナブルExcelVBAについて書きたい気持ち」です。楽しみ。

ちなみにあと2日ぶん枠があるので、今からでも登録できます。1週間経過現在、異世界転生・番組表・ライフログ・GAS・異世界転生・aikoと実に多彩なラインナップをみせております。ぜひ。

adventar.org