【二階堂メーカー】どうか素敵なフレーズが作られますように

みなさん、大分麦焼酎二階堂🍶のCMを見たことはありますか？  
私がみた中でCMで印象に残ってるのは[こちらのCM](https://nikaido-shuzo.co.jp/library/cm2011_2.html)で「どうか、タイムマシンが発明されませんように。」というフレーズが強烈に印象に残っています。

公式のCMライブラリーを見てみると、いい感じにフレーズが区切られていて、これランダムに組み合わせても意味通じるんじゃね？と思い、二階堂メーカーというものを作ってみました。  
下記にデプロイしていますので、アクセスしてみてください。（リロードするたびに内容が変わります。）

https://nikaido-maker.vercel.app/

今回は二階堂メーカー実装に関するポイントを要所で紹介します。

## 仕様パート1
まずランダムの前提として、各篇の`.library_text`を1行区切りを1フレーズとしてランダムに組み合わせを行います。
![スクリーンショット 2022-12-20 12.55.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359978/75e68c6c-6ebf-51ae-6287-90dda3061517.png)
ランダムと言っても単純にすべてのフレーズをごちゃ混ぜにして抽選するのではなく、各篇の上から1行ずつ組み合わせてランダムにすることにしました。  
というのも仮に各偏の1行目が連続してしまうことや、終わりの方に冒頭1行目が出てくると、全体としてクオリティが低下しまうのでは？と思ったからです。

下図のような感じで組み合わせを行います。
![sp01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359978/fa201c09-7e79-2033-9314-faaaa01bea01.png)
細かい仕様は後に解説しますが、とりあえずこれだけ理解しておけば、上記サイトで遊べるかなと思います。

## データ準備(Python)
公式サイトよりデータを取得するために、**BeautifulSoup**を使って各ページのコンテンツを取得しました。（いまだに`Beautiful`をタイポするのをなんとかしたい。）

### インデント問題
`.library_text`内のテキストを取ってくる時に困ったのがインデントです。
下記のような感じでテキスト情報だけ取得した際に、ソース上のインデントもテキストとして見なされてしまいました。

```python:main.py
data = requests.get("https://nikaido-shuzo.co.jp/library/cm2011_2.html")
html = BeautifulSoup(data.content, "html.parser")
library_text = html.select_one(".library_text").text
```
![スクリーンショット 2022-12-20 13.25.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359978/83e12f12-3257-88bb-7327-30b63ddb8064.png)
赤いところがインデント

インデントを除去しようと思って、ググって出てきた`textwrap.dedent`を利用してみたところ、1行目のインデントしか削ってくれなかったです。参考記事いくつか見た中でヒアドキュメントの例しかなかったので、用途が違ったかも。
![スクリーンショット 2022-12-20 13.27.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359978/c81ae302-8211-c965-5c04-d52cee90ed49.png)
仕方ないので今回は空白にあたる文字は全て除去することにしました。  
中には文中に空白があるものもありましたが、~~めんどくさいので~~一旦は気にしない方向で。

二階堂メーカーでは、`.library_text`の情報さえあればコンテンツとして完結するのですが、一応、コンテンツとして取れるものは取っておこうと思い、最終的には下記の様な構造になりました。

```json:nikaido.json
[
    {
        "id": 41,
        "url": "https://nikaido-shuzo.co.jp/library/cm2022.html",
        "title": "2022年「水のため息」篇",
        "library_text": "どこにでもある風景。\nここにしかない風景。\n静けさは語りかけ、\n流れは\nささやきに変わる。\nむぎ100％\n大分むぎ焼酎二階堂\n",
        "split_library_text": [
            "どこにでもある風景。",
            "ここにしかない風景。",
            "静けさは語りかけ、",
            "流れは",
            "ささやきに変わる。",
            "むぎ100％",
            "大分むぎ焼酎二階堂"
        ],
        "line_count": 7,
        "locations": [
            {
                "img": "/images/library/2022/1.jpg",
                "name": "福岡県朝倉市千手\n小石原川"
            },
            {
                "img": "/images/library/2022/2.jpg",
                "name": "福岡県朝倉市秋月\n野鳥川"
            },
            ...省略
        ],
        "movie": "https://www.youtube.com/watch?v=IU2lermMR4c"
    },
    ...省略
]
```
**各項目で補足**

- id: 今後CMが増えたときにずれないように、新しい篇から値を大きく。
- library_text: 改行で区切る前の元テキスト。
- split_library_text: 今回の主役。`library_text`を改行区切りで配列化したもの。
- line_count: `split_library_text.length`で取得可能だが、処理として先に出力できるものはしておく。
- locations.name: 空の`p.name`があるので、textがなければ`locations.img`のみ。
- movie: 動画はない場合もある。`watch?v={id}`となっている部分は、実際には`embed/{id}`。

## 仕様パート2
`line_count`を入れた理由としては、生成するフレーズの行数を各篇の`line_count`範囲内でランダムに出力したかったためです。  
サイトでリロードするたびに行数が変わるのは、単純にランダムな行数ではなく、この様な仕様となっています。

### ここまでの仕様
1. 各篇の`line_count`範囲内で生成される行数を確定させる
2. 1.の行数内で各篇の上から1行ずつ組み合わせでランダムに生成

## データ整理（👀✋）
データができた後、一通り内容を確認すると下記のことに気づきました。

1. `line_count`が4〜13・15行ある
1. たまに表記のゆれがある。
   1. 「麦100%」「むぎ100％」「むぎ１００％」「むぎ１００％。」etc...
   2. 「大分むぎ焼酎二階堂」「大分むぎ焼酎、二階堂」
1. 末尾2行がセットになってる場合が大半
   1. 中でも「むぎ100％」が多い
   2. たまにセットが1行になってることがある。<br>![スクリーンショット 2022-12-20 13.45.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359978/5b34554f-59ae-8991-8698-fdb26134041d.png)


### 1.について
15行のパターンが1つしかないことから、「1. 各篇の`line_count`範囲内で生成される行数を確定させる」で15行生成をするパターン引いた場合、末尾を必ず15行のものを使わないといけない（14行の篇もないので、実際は14・15行を利用する）ことになるので、2行結合して13行まで削りました。

### 2.について
味かなと思い、ノータッチの方向で。

### 3.について
今回は以下の様な理由から、末尾2行をセットとする仕様を採用しました。  
なので、まずは各篇の`split_library_text`の末尾2行分を取ってきます。

1. 公式に敬意を払い、公式の素敵なフレーズを崩したくない。
2. セットになっていない場合でも、1行目からフレーズを重ねていった場合、必ず末尾が**句点**で終わることができる。

ちなみに3-2については手で2行に分けました。

## 仕様パート3（確定）
上記のため、最終的な仕様は下記のようになりました。

1. 各篇の`line_count`範囲内で生成される行数を確定させる（4〜13行）
2. どれかの篇から末尾2行取得する
3. 1.の行数から`-2`した行数内で各篇の上から1行ずつ組み合わせでランダムに生成

## フロントエンド・API(Next.js)
今回、APIを作る前提だったのでNext.jsを採用しました。  
### エンドポイント
```
GET https://nikaido-maker.vercel.app/api/random
```

### レスポンス例
```
{
    "result": [
        "どこにでもある風景。",
        "会いたい人と会えない人。",
        "ふるさとは、私の中に流れています。",
        "麦100%。",
        "大分むぎ焼酎、二階堂"
    ]
}
```

※Vercelの稼働状況を見て、APIを公開停止する可能性があります。  
※フロントエンド側はAPIを叩いているわけではなく、APIと同じ関数を呼んでるだけなので、いくら更新してもAPI制限については心配ありません。

### Mantine
CSS書きたくない！という思いから、大した画面を作るわけではないのに[Mantine](https://mantine.dev/)を導入しました。結局ほとんどが自分でスタイル書かないといけなくて、[Tailwind CSS](https://tailwindcss.com/)にしとけばよかったなと後悔。

```
TODO: Tailwind CSSへ変更
```

## まとめ
試していただくとわかると思うんですが、思ったより素敵なフレーズが誕生せず期待した結果が得られませんでした😨  
ただ、[こちら](https://nikaido-shuzo.co.jp/library/cm2017_02.html)の7行目`詩人　長田弘はこう書きました。`が出てきたときに、ぐっと引き締まる感じがしました。

Pythonでディープラーニングを用いて解析すれば、もっと素敵な感じにできるんだろうなぁと思いながらも、専門外なので他の方へ託します。

https://nikaido-maker.vercel.app/

## 最後に
### GoQSystemでは一緒に働いてくれる仲間を募集中です！
ご興味がある方は以下リンクよりご確認ください。

https://goqsystem.recruit.site/?qiita=a4afeb0af3d7868c1486