LoginSignup
10
15

More than 3 years have passed since last update.

【Python】自然言語処理でグルメ大国福岡の人気店を発掘してみた

Last updated at Posted at 2019-11-28

概要

Fukuokaの「食」と言えば、ラーメン・もつ鍋・ごまさばですよね。福岡グルメとは裏腹に私はこよなくうなぎを愛しており、15年間行きつけの鰻屋さんに通っています。鰻屋の新規開拓をしたいところですが、金銭的に余裕のない私にはちょっと、、、なので今回は福岡の鰻屋さんの食べログ口コミを使って、行きつけのうなぎ屋さんに類似したお店を発掘してみました。そして、実際に行ってみた感想を最後に述べたいと思います。

行きつけの鰻屋のご紹介

・福岡県久留米市にある
「富松うなぎ屋」という約80年続く老舗
・席に着くとまずは「うなぎの骨」を堪能
・ほどよい塩加減とやみつきになるサクサクな食感が堪らない
・オススメメニューはセイロむし(2400円)と鯉のアライ(550円)
・ふっくらもちもちの肉厚うなぎに80年継ぎ足された秘伝のタレはまさに鬼に金棒
・鰻に甘いタレがたっぷり染み込んでおり、口に入れると香りが一層引き立ち最高の味わいです。

S__254541826.jpg
eel.png

空腹で気が狂いそうなので、気を紛らわすために作業開始w

環境

・Python 3.7.4
・windows10

参考資料

【Python】🍜機械学習で「隠れた名店」を探してみた。(そして実際に行ってみた)🍜

大まかな流れ

  1. 食べログ口コミを取得(スクレイピング)
  2. 口コミを単語にする(形態素解析)
  3. レコメンド(Word2vec)
  4. そして、実際に行ってみた!

1. 食べログ口コミを取得(スクレイピング)

食べログサイトから福岡県のうなぎ屋さん全ページを取得しました。

スクレイピングの流れは以下の通り。
1. 店舗一覧ページ
2. 店舗詳細ページ
3. 口コミ一覧ページ
4. 口コミ詳細ページ
詳しいことはこちらを参照。

また、前回の記事でご指摘頂いたので、サーバーに負荷をかけないよう1秒待機するようにしました。

scraping_eel.py
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time

list_df = pd.DataFrame(columns=['店舗名', '口コミ'])

name_list = []
link_list = []

# 店舗一覧ページ
for page in range(1, 30):
    url = 'https://tabelog.com/fukuoka/rstLst/unagi/' + str(page) + '/?svd=20191026&svt=1900&svps=2'
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'lxml')
    # 店舗一覧ページから店舗名一覧と店舗詳細ページのリンクを取得
    store_tags = soup.find_all('a', class_='list-rst__rst-name-target cpy-rst-name')
    for store_tag in store_tags:
        # 店舗詳細ページのリンクが格納されたリスト作成
        link_list.append(store_tag.get('href'))
        # 店舗名一覧が格納されたリスト作成
        store_name = store_tag.text
        store_name = store_name.replace(' ', '')
        store_name = store_name.replace(' ', '')
        name_list.append(store_name)

    for store_name, link in zip(name_list, link_list):
        # 店舗詳細ページ
        response = requests.get(link)
        soup = BeautifulSoup(response.text, 'lxml')
        # 店舗詳細ページから口コミ一覧ページへのリンクを取得
        review_tag_id = soup.find('li', id='rdnavi-review')
        review_tag_href = review_tag_id.a.get('href')

        # 口コミ一覧ページ
        response = requests.get(review_tag_href)
        soup = BeautifulSoup(response.text, 'lxml')
        # 口コミ一覧ページから口コミ詳細ページへのリンク取得
        review_comments = soup.find_all('a', class_='rvw-item__title-target')
        for review_comment in review_comments:

            # 口コミ詳細ページ
            response = requests.get('https://tabelog.com' + review_comment.get('href'))
            soup = BeautifulSoup(response.text, 'lxml')
            # 口コミ取得
            review_tags = soup.find_all('div', class_='rvw-item__rvw-comment')
            review = review_tags[0].p.text.encode('cp932', 'ignore')
            review = review.decode('cp932')
            review = review.replace('\n','')
            review = review.replace(' ','')
            review = review.replace(' ','')

            # サーバーに負荷を与えないため1秒待機
            time.sleep(1)

            # 取得した項目
            # store_name=店舗名
            # review=口コミ
            tmp_se = pd.DataFrame([store_name, review], index=list_df.columns).T
            list_df = list_df.append(tmp_se)

print(list_df)

# CSV保存
list_df.to_csv('list_eel.csv', mode = 'a', encoding='cp932')

2. 口コミを単語にする(形態素解析)

morphological_analysis_eel.py
from janome.tokenizer import Tokenizer
from gensim.models import word2vec
import pandas as pd

#pandasで作成したCSVファイルを読み込み
df_file = pd.read_csv('list_eel.csv', encoding='cp932')

store_names = df_file['店舗名'].tolist()
reviews = df_file['口コミ'].tolist()

t = Tokenizer()

result = []

#解析開始
for i, review in enumerate(reviews):
    s = review
    tokens = t.tokenize(s)

    r = []

    for tok in tokens:
        #単語ごとに分かち書き
        #活用形がない(名詞)のとき実行
        if tok.base_form == '*':
            word = tok.surface
        #活用形がある(動詞・形容詞)のとき実行
        else:
            word = tok.base_form

        ps = tok.part_of_speech

        #品詞の種類を取得
        hinshi = ps.split(',')[0]

        #[]内の品詞のみを処理して追加
        if hinshi in ['名詞', '形容詞', '動詞']:
            r.append(word)

    rl = (' '.join(r)).strip()
    result.append(store_names[i])
    result.append(rl)
    #余計な文字コードを排除
    result = [i.replace('\u3000','') for i in result]

print(result)

#書き込み先テキストファイルを開く
text_file = 'wakati_list_eel.txt'
with open(text_file, 'w', encoding='utf-8') as fp:
    fp.write("\n".join(result))

#テキストファイルを読み込み、model作成
data = word2vec.LineSentence(text_file)
model = word2vec.Word2Vec(data, size=200, window=1, hs=1, min_count=1, sg=1)
model.save('./wakati_list_eel.model')

3. レコメンド(Word2vec)

word2vec_eel.py
from gensim.models import word2vec
import pandas as pd

model = word2vec.Word2Vec.load('./wakati_list_eel.model')

df_file = pd.read_csv('list_eel.csv', encoding='cp932')
store_names = df_file['店舗名'].tolist()

#任意の店舗名
keyword = '富松うなぎ屋黒田本店'

dic = {}

for store_name in store_names:
    result = model.wv.similarity(store_name, keyword)

    #出力結果を[店名: 類似度]にしたい
    dic[store_name] = result

#類似度が高い順で上位5番目まで取得
result = sorted(dic.items(), key=lambda x: -x[1])[1:6]
print(result)
>>>
[('福泉操', 0.16873132),
('博多銀丁', 0.14888753),
('うなぎ処安', 0.13577707),
('魚国', 0.12946151),
('うなぎ処柳川屋博多駅前店', 0.12561372)]

結果発表!
1位:福泉操
2位:博多銀丁
3位:うなぎ処安
4位:魚国
5位:うなぎ処柳川屋博多駅前店

ということで、堂々たる1位に輝いた「福泉操」に行ってみました。

4. そして、実際に行ってみた!

柳川駅から徒歩1分という好立地に店を構える「福泉操」
うなぎの看板が際立ち、迷うことはまずない
・店内に入ると店中に香ばしい香りが充満し食欲そそられるぅ
・安定のうなぎのセイロむし(2800円)を注文
・我慢できないので早速一口いただきます
口全体に鰻の風味が広がり幸せパンチ
ふっくら香ばしくて旨い
・さすが駅前の老舗。大満足です!
・自宅から車で2時間かけて行った甲斐がありました
・みなさんも柳川に来た際はぜひ、ご堪能ください!

福泉操: https://fukusensou.com/
S__254795831.jpg
S__254795829.jpg

私からのオススメ店 in Fukuoka

ちんぷんかんぷん(焼き鳥)
京風もつ鍋越後屋
味一(ラーメン)
らーめん雷蔵
安全食堂(ラーメン)
博多もつ鍋前田屋のもつラーメン

ラーメンばっかになっちゃった笑
福岡に来た際には是非、訪れてみてください。一人で不安な方はご連絡下さい。お供します!!

おわりに

今回はうなぎ屋の新規開拓を目的にスクレイピング、形態素解析、word2vecを学びました。新規開拓に戸惑ったらこれを使うといいかもしれませんね。今後は類似度の精度向上を図るため、ワードの絞り込みやTF-IDFを加え実装してみたいと思います。長くなりましたが、ここまで読んでくださりありがとうございます。誤っている箇所がございましたら、コメントでご指摘頂けると大変嬉しいです。

10
15
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
10
15