64
46

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

目的

QBハウスがもたらす「省時間」効果を最大限に享受するには、QBハウスでの「待ち時間」を最小限にする必要があります。

QB HOUSEとは? | QB Lab | QBハウス "10分の身だしなみ"
QB HOUSE が考える「省時間」とは、同じ社会的・経済的効果や便益を、
より少ない時間で得られるヘアカットサービスを提供することです。
このクオリティを高めていくために、私たちは多様な取り組みをスタートしています。

そのため、公式サイトでは店舗ごとの待ち人数を確認することができます。

ご利用案内 | QBハウス "10分の身だしなみ"
image.png

渋谷南口店 | 店舗検索結果 | QBハウス "10分の身だしなみ"
image.png

ただ、もったいないのは「現在の」待ち人数しか確認できません。

QB PREMIUM では、アプリ予約ができるようですが、始まったばかりのサービスのため、QBハウスで待ち時間なく対応してもらうためには、ユーザ自身で工夫が必要です。

今回は、このサイトのデータを使って待ち人数の推移を可視化することで、空いている時間帯の目測を立てられることを目的とします。

成果物

開店直後はとても混んでいるようなので、11:00-12:00 に早めの昼食を済ませるついでにQBハウスに寄るなどの有効活用が考えられます。

image.png

Cloudflare スタック

練習も兼ねて色々なコンポーネントを使ってみます。

ブラウザと Cloudflare だけあれば、色々とやりたいことができるのがいいところです。

また、使いたい機能も Workers に Binding をするだけですぐに使えるようになります。

Cron Workers

以下の Cron Trigger の記述により、5分ごとに起動させます。

wrangler.toml
[triggers]
crons = [ "*/5 * * * * " ]

Browser Rendering

上記リンクを参考にして、待ち人数を定期的にヘッドレスブラウザから取得するコードです。

(今回の用途では、Browser Rendering である必要はないかもしれません)

// binding in wrangler.toml: browser = { binding = "MYBROWSER" }
const browser = await puppeteer.launch(env.MYBROWSER);
const page = await browser.newPage();
// xxx は店舗ページのID
await page.goto("https://www.qbhouse.co.jp/search/xxx", {
    //一定時間ネットワーク通信のないことで完了を判定する
    waitUntil: "load",
});
// セレクタで待ち人数の要素を HTML から取得
const html = await page.$eval('#salon_info > div > div:nth-child(4) > div.waiting_wrap > dl.number > dd', node => node.innerText);
//console.log(`shop status in html === ${html}`)
...
// D1 WRITE 処理
...
await browser.close();

D1

オープンベータになりましたね。

データベース準備

以下のコマンドでテーブルを用意します。

#データベース作成
wrangler d1 create QBHOUSE
#テーブル作成
wrangler d1 execute QBHOUSE --command "CREATE TABLE IF NOT EXISTS WaitPeople (DateTime DATE PRIMARY KEY, WaitPeopleNumber INTEGER)"
#テーブル一覧取得
wrangler d1 execute QBHOUSE --command "SELECT name FROM sqlite_schema WHERE type ='table'"
#レコード一覧取得
wrangler d1 execute QBHOUSE --command="SELECT * FROM WaitPeople"
#┌─────────────────────┬──────────────────┐
#│ DateTime            │ WaitPeopleNumber │
#├─────────────────────┼──────────────────┤
#│ 2023-10-02 14:35:00 │ 4                │
#├─────────────────────┼──────────────────┤
#...
#日付ごとのレコード一覧取得
wrangler d1 execute QBHOUSE --command="SELECT substr(DateTime,12,2) AS hour, substr(DateTime,15,2) AS min, substr(DateTime,18,3) AS sec, WaitPeopleNumber AS num FROM WaitPeople WHERE DATE(DateTime) = '2023-10-02'"
#┌──────┬─────┬─────┬──────────────────┐
#│ hour │ min │ sec │ num │
#├──────┼─────┼─────┼──────────────────┤
#│ 14   │ 35  │ 00  │ 4                │
#├──────┼─────┼─────┼──────────────────┤
#...

データ書き込み

以下のようなコードでシンプルに書き込みが可能です。

(データが溜まってきたら、定期的に古いデータを削除する仕組みが必要かもしれません)

if (html != '開店前' && html != '受付終了') {
		// html is 4\n人
		await env.DB
			.prepare('INSERT INTO WaitPeople (DateTime, WaitPeopleNumber) VALUES (?1, ?2)')
			.bind(dateInYyyyMmDdHhMmSs(jstNow), html.slice(0, 1))
			.run()
	}

Fetch Workers

?date=2023-10-03 のようにクエリをつけると、日付ごとの JSON データを返してくれる API を作成します。

JSON Response

以下のコードで D1 から読み込んだデータを JSON に変換してレスポンスします。

// Get query parameter like ?date=2023-10-03
const { searchParams } = new URL(request.url)
let date = searchParams.get('date')
// Get data from D1 database
const stmtstring = `SELECT substr(DateTime,12,2) AS hour, substr(DateTime,15,2) AS min, substr(DateTime,18,3) AS sec, WaitPeopleNumber as num FROM WaitPeople WHERE DATE(DateTime) = '${date}'`
const stmt = env.DB.prepare(stmtstring);
// Transform Data into JSON
const { results } = await stmt.all();
const json = JSON.stringify(results, null, 2);

// Return JSON Response 
return new Response(json, {
    headers: {
        "content-type": "application/json;charset=UTF-8",
        'Access-Control-Allow-Origin': 'https://busy-qbhouse.pages.dev'
    },
});

CORS

以下のコードを流用しました。

Pages

以下のフォルダと HTML ファイルを作成し、アップロードします。

busy-qbhouse-pages/
└── index.html
wrangler pages project create busy-qbhouse
wrangler pages publish .

drawChart (Google Chart API)

これは感動しました。HTML のような、以前からコードの書き方が広く知れ渡っていて、最新情報に依存しにくい部分はChatGPTを使って基本形を作ってもらって、そこから最新の記述方式に変えていくことで効率的に実装できました。

ChatGPTに投げたプロンプト1
AJAXを使用してデータを取得し、Google Chart API で折れ線グラフを書くためのhtmlコードを書いてください。
・タイトルは「QBハウスxxx店の待ち人数推移」
・縦軸の名称は「待ち人数」
・縦軸の範囲は0から10
・横軸の名称は「時間」
・横軸のタイプは timeofday
・横軸の範囲は09:00から20:00
・幅は700px

・軸ラベルを表示して。

・各要素の線の色は洒落た感じで。

・コードはなるべく簡潔に。

また、時系列データに関するオプション等は以下のサイトを参考にしました。

まとめ

どこかから定期的に情報と取ってきてデータを貯める、その後ほしいデータを可視化するパターンは、このような形で手軽に実装できることがわかりました。

実際にやってみると、Cloudflare だけあれば何の不自由もなくできてしまいます。
やはり圧倒的な開発者体験でした。

64
46
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
64
46

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?