「こだわりの◯◯」とかの浅い言葉が嫌いなコーヒー屋は、クリシェをプログラムで炙り出すことにした。
ツキシマでーす。
また今年も花粉の季節なので、あまり店は開けずにプログラムでも書いていましょう。
前置き:みんなテレビに影響されすぎてか、時々おかしな質問が来る。
うちのコーヒー屋に初めて来た人が「この店のコンセプトは何ですか?」とか聞いてくることがあって、『コンセプト!特にないよ!』って返すんですが、もしあったとして言葉で説明したくないですよね。
あと、「趣味が高じてカフェを始めたとかですか?」「こだわりのコーヒーを近所の人に楽しんで欲しくてとかですか?」みたいな、(はいそうですね)の返事を期待した誘導尋問が来たりするんですが、正直に『いや全然!』って答えております。
「趣味が高じての〇〇」「こだわりの〇〇」みたいな言葉って世の中で乱用されすぎて使うのが恥ずかしいのと、むしろこれに続く内容が陳腐化すると思うので自分では絶対に言わない言葉です。
こういう乱用された結果、ありきたりになった常套句のことを「クリシェ:cliché」と言います。
他にも、「○○は苦手だけど、ここのは食べられる!」とか「ここの○○を食べに、わざわざ北海道から来る人もいる」(最遠方コンテスト)みたいなのもクリシェですね。
本題:note API で「定番」カテゴリの記事をかき集めて、一般的なコーヒー屋のクリシェを探そう。
note の「定番」に記事を分類するアルゴリズムは分かりませんが、きっと定番たりうる文章がまとまってるはず。「定番カテゴリの頻出の言葉で作った文章=クリシェ」になるんじゃないかと仮定してプログラムを書いてみます。
こっそり用意されている note API があったので、MATLAB で呼び出してみましょう。
API があればとりあえず叩いてみるコーヒー屋です。(コンセプト)
プログラム①:定番タブにある記事の本文を400件くらい取ってこよう。
検索ワードは「カフェ巡り」でいいかな。まずは、データ取得のプログラムをササッと書いて400件くらいの記事を取ってみよう。
%% 「カフェ巡り」の検索ワードで一覧を取ってくる
body = [];
s = 20;
for x = 0:s:s*19
% Sort -> HOT:急上昇 NEW:新着 LIKE:定番
cafe = webread(['https://note.com/api/v3/searches?context=note&q="カフェ巡り"&size=',num2str(s),'&start=',num2str(x),'&sort=like']);
for n = 1:height(cafe.data.notes.contents)
note = webread(['https://note.com/api/v3/notes/',cafe.data.notes.contents(n).key]);
b = note.data.body;
if isempty(b) % 本文なしと有料は空白になっちゃうのでスキップ。
bd(n) = string;
else
bd(n) = string(b);
end
end
body = [body, bd];
pause(1) % ちょっとゆっくり。
end
body = eraseTags(body); % タグ捨て
検索結果の Body は先頭から240文字程度しか取れないので、各記事毎に webread をして body を再取得しています。
取れてるか確認。
>> body'
ans =
400×1 の string 配列
""
" 「金沢、遊びに来てよ!」「カフェ巡りしよう〜」 石川県で一人暮らしをしている妹に誘われて、
" 今回のひとり旅の目的地は3年ぶりの東京。行きたい気持ちをぐっと我慢してSNSを眺めてる日々でしたが、
""
" 1泊2日の女子旅で鎌倉旅行へ。 ずっと行きたいと思っていた念願のお店を巡りました。
" ちょうど一年前の今時分。 爽やかな新緑の京都でカフェ巡りをした。 まだ外国人観光客はまばらで、
" もう何度も書いてるし、それ以上に常に悩んでいる。 カフェ巡りを存分に楽しみたい一方で、
長いので表示をカットしてますけど取れてますね!
空っぽの行は「写真+キャプション」だけの記事か、有料記事で取れなかったものです。
プログラム②:かき集めたデータを単語にバラして、名詞だけ取ってみる。
カフェ巡りの定番の紹介文に頻出する「名詞」がクリシェを構成してるんじゃないかと思うので、Bag-of-Words で名詞だけまとめてみましょう。
%% トークン化して余計な情報を減らす
documents = tokenizedDocument(body);
documents = addPartOfSpeechDetails(documents);
documents = normalizeWords(documents,Style="lemma");
documents = erasePunctuation(documents);
documents = removeStopWords(documents);
documents = removeShortWords(documents,2);
%% 品詞でまとめる (noun/verb/proper-noun)
dt = tokenDetails(documents);
noun = dt(dt.PartOfSpeech == 'noun',:);
bag = bagOfWords(noun.Lemma');
bag = removeEmptyDocuments(bag);
bag = removeInfrequentWords(bag,3);
wc = wordcloud(bag);
ワードクラウド表示の結果↓↓
「カフェ巡り」ってキーワードなので「カフェ」「コーヒー」「喫茶店」「メニュー」なんかは頻出して当然として、「おすすめ」「ランチ」「こだわり」「雰囲気」「スイーツ」「おしゃれ」あたりを駆使すれば「ありきたりでどっかで見たような文章=クリシェ」ができるのではないでしょうか。
プログラム③:上記の結果をふんだんに使った架空のカフェの紹介文を ChatGPT に書いてもらおう。
適当な閾値で頻出名詞を20個くらい抽出して、ChatGPT に投げてみよう。400個の記事で、74回以上出現した名詞が21個ありました。
>> bag.Vocabulary(bag.Counts > 73)'
ans =
21×1 の string 配列
"カフェ"
"スイーツ"
"チーズ"
"ケーキ"
"コーヒー"
"雰囲気"
"アイス"
"メニュー"
"ランチ"
"ドリンク"
"こだわり"
"オープン"
"パフェ"
"おすすめ"
"プリン"
"大好き"
"クリーム"
"喫茶店"
"イベント"
"オススメ"
"おしゃれ"
ChatGPT はプログラムで連携させてもいいけど、一言なのでチャットウィンドウに投げたほうが楽ですね。ケーキとかプリンとか具体的なメニューも多いですが、文章をシンプルにするために具体的な甘いものは省きます。
結果↓↓
かつてどこかで見たことがあるような、典型的な喫茶店の紹介文ができました!
特に「おしゃれな喫茶店」「多くのお客様に愛されています」「こだわりのコーヒー」あたりって、何かを言っているようで何も言ってないのと同じなのかもしれない。
まとめ:浅い文章は自動生成できるし、響く文章はきっとここに出てこないワードで出来ている。
プログラムで「クリシェになりうる単語の探索」と「浅い文章の自動生成」までできましたが、この自動生成された平均的で面白みがない紹介文みたいな文章を書かないようにもっと表現を磨こうと思いました。
ということで、試しにうちのコーヒー屋の頻出キーワードを入れて紹介文を書いてもらおうとしたら ChatGPT に断られました・・・