Firebaseから相席ラウンジのデータをスクレイピングしFlutterアプリで表示する

**出会いがない！相席屋にいってみたい！**でもどこの店舗に行ったらいいかわからない！そんな人のために簡単なアプリを**FirebaseとFlutter**で作ってみました。

- [スクレイピングのサンプルコード](https://github.com/kboy-silvergym/Aiseki-Scraping)
- [Flutterのサンプルコード](https://github.com/kboy-silvergym/AisekiBlueOcean)

[動画](https://www.youtube.com/watch?v=riJ8ogQiagQ)でもかなりわかりやすく説明したつもりなので、5分時間がある方はチェックしてみてください。

[【相席屋必勝法】プログラミングで相席ラウンジの女子率を計測ww【スクレイピング】](https://www.youtube.com/watch?v=riJ8ogQiagQ)

## 目的
![ブルーオーシャン.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/eef941d8-eebc-ff88-2e00-43cdbcbc105e.jpeg)

出会いがない！相席屋にいってみたい！でもどこの店舗に行ったらいいかわからない！そんな人のために、**ブルーオーシャン順**に並べたアプリを作ろう！というのが目的です。

また、最速で作るために、**FirebaseのCloud FunctionとFirestoreだけ**で作っていきます。

## 全体構成

firebase cloud functionに毎5分ごとに実行するfunctionを作り、5分ごとに相席ラウンジのサイトをスクレイピングし、相席ラウンジの各店舗の現在の男女の人数を取得します。このデータをfirebaseのfirestoreと呼ばれるデーターベースに記録していきます。

![全体構成.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/8ed91828-69f1-9913-28ba-e2afd90c14d1.jpeg)

データーベースに保存された情報を可視化するために今回はFlutterでスマホアプリを作ります。スマホアプリで現在のブルーオーシャン店舗ランキングを簡易的に表示し、今行くべき店舗がすぐに分かるようにするアプリを作って行きましょう。

## 定期実行

Firebaseのドキュメントに[関数のスケジュール設定](https://firebase.google.com/docs/functions/schedule-functions?hl=ja)にあるように、最近Firebase Cloud Functionsに定期実行の機能が追加されたため、容易に定期実行をできるようになりました。

（以前はAppEngineを組み合わせて定期実行していた - [FirebaseとAppEngineだけでRSSを使った動画アプリやニュースアプリを作る方法](https://qiita.com/k-boy/items/c3569e2e7dca0b9e08c4)）

例えば、15分ごとに実行したい場合は、以下のように書きます。

```typescript
exports.scrapeAiseki = functions.pubsub
    .schedule('every 15 minutes')
    .timeZone('Asia/Tokyo')
    .onRun(async (context) => {

    });
```

## スクレイピング

スクレイピングとは、あるWEBサイトのHTMLの要素から必要な情報を抽出することを言います。今回は相席ラウンジのHPのHTMLを抽出して、店舗ごとの人数比などを取得していきましょう。というのも、相席ラウンジのトップページからは各店舗ごとの男女比が一目瞭然なのです。

![相席男女比.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/902e6bb7-940f-0ef8-0e57-5ef3613ba91d.jpeg)

今回スクレイピングにはpuppeteerというライブラリを使います。

![puppteer.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/029d770a-df87-da9e-f213-0b5cd6ae5f0d.jpeg)

簡単に説明すると、puppeteerはヘッドレスブラウザと呼ばれるChromeやSafariみたいなGUIはないブラウザです。つまり目には見えないブラウザで、puppeteerが指定したwebページのサイトを見た目には表示せずに見に行くことができます。

こちらをfirebaseのcloud functionで使いたいので、package.jsonのdependenciesに以下のようにpuppeteerを追加しターミナルでnpm installします。

```json:package.json
  "dependencies": {
    "firebase-admin": "~7.0.0",
    "firebase-functions": "^2.3.0",
    "puppeteer": "^1.20.0"
  }
```

これで使えるようになったのでFirebase Cloud Functionsの定期実行のファンクションの中身に以下のように書いてスクレイピングします。

```typescript
exports.scrapeAiseki = functions.pubsub
    .schedule('every 15 minutes')
    .timeZone('Asia/Tokyo')
    .onRun(async (context) => {
        const browser = await puppeteer.launch({ args: ['--no-sandbox'] });
        const page = await browser.newPage();
        await page.goto('https://oriental-lounge.com');

        // 店舗取得
        const shops: [any] = await page.$$('.shop');

        await Promise.all(shops.map(async shop => await setShopData(shop)));
        await browser.close();
    });
```

上記コードでは、

```typescript
await setShopData(shop)
```

でもろもろ店舗名や男女比を取得しています。その辺のコードは割愛して[サンプルコード](https://github.com/kboy-silvergym/Aiseki-Scraping)をみていただきたいと思いますが、何をやっているかだけ説明していきます。

Chromeで右クリックして検証モードを開いた、下図を見てください。

![スクレイピング対象1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/957d6058-1249-2a27-4749-6d816e7658f3.jpeg)

各都市の人数を表示しているところはshop_listというclass名のdivタグに囲まれています。そして各店舗の要素はshopというclass名のdivタグに囲まれています。

shopの中を展開していくとshop_name、num、link_detailというclassがあり、その名の通りshop_nameには店舗名が入っており、札幌という日本語名、そしてその中にはenというクラスで囲まれた英語表記があります。

![スクレイピング対象.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/74a7f669-0fd2-e683-4772-f6e4e1ce5a1e.jpeg)

またnumの中身を展開すると、その中にはmanとwomanがあります。manの中のspanタグの間に人数が入っています。womanも同様です。こちらから人数が抜き出せそうですね。

すなわち今回取るべきところは、shop_nameの中のenのspanタグに挟まれた部分とmanとwomanの中のspanタグに囲まれた数字です。

これらをスクレイピングてfirestoreに都市ごとに保存していきます。詳しくは[サンプルコード](https://github.com/kboy-silvergym/Aiseki-Scraping)で。

## Firestoreに保存

Firestoreに保存されると、firebaseの管理画面からこのように保存されたデータを確認することができます。しっかり書く都市のデータが保存されていますね

![firestoreの状態.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/1d05ee38-08cf-40d4-ffc1-087b28c9f6a0.jpeg)

## Flutterアプリ

最後にこのデータをアプリから表示していきます！今回のアプリ開発で使ったフレームワークがFlutterです。

![flutter.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111366/33f0c287-5391-6a42-c101-5950e277f46d.jpeg)

今回は、Flutterを使って、相席ラウンジの各都市のデータをブルーオーシャンな順にリストで表示していくというシンプルな構成です。

<img src="https://user-images.githubusercontent.com/17683316/68387193-1ad8ce00-01a1-11ea-9922-86b8762b2d86.gif" width=200>

取得したデータは最後に女性に比べて男性が少ない順に並べ替えます。つまりブルーオーシャン。最後にcellに都市名と男女の人数を表示して完成です。

Flutterのサンプルコードは、[こちら](https://github.com/kboy-silvergym/AisekiBlueOcean)。

## まとめ

ってことで、！良い相席ラウンジライフを！

よければ[動画](https://www.youtube.com/watch?v=riJ8ogQiagQ)も見てください！

## リンク
- [【相席屋必勝法】プログラミングで相席ラウンジの女子率を計測ww【スクレイピング】](https://www.youtube.com/watch?v=riJ8ogQiagQ)
- [スクレイピングのサンプルコード](https://github.com/kboy-silvergym/Aiseki-Scraping)
- [Flutterのサンプルコード](https://github.com/kboy-silvergym/AisekiBlueOcean)
- https://oriental-lounge.com/