星とか面白いこととか

暇なのでいろいろ書きます

星とか面白いこととか

スポンサーリンク

食べログ3.6の店だけを表示するマップを作りました【食べログ3.6問題】

スポンサーリンク

スポンサーリンク

f:id:Marimofmof:20191011124917p:plain

こんにちは。まりもです。

最近Twitterで盛り上がりを見せている[食べログ3.6問題]

その概要は以下の通りです。

 

食べログ評価3.8以上のお店は年会費を払わなければ3.6に下げられる
食べログが評価を恣意的に操作しているかもしれない

 

ということです。

clean-copy-of-onenote.hatenablog.com

詳しくは上記の検証記事を参照ください。

要は

食べログの評価が3.8を越えると、年会費を払うことを要求され、払わないと3.6に下げらる
年会費に関しての真偽は定かではないが、実際に評価3.6の店が突出して多い。高い確率で事実であることがわかる

 ということです。

食べログ4.2の店とかに行って、「あれ? 思ったほどでもないな」って経験はこういう理由からきているのかもしれません(諸説あり)

立論

このことから僕は一つの仮説を生み出しました。

 

f:id:Marimofmof:20191011161545j:plain

です。

 

評価3.8以上ということは、間違いなく食べログに上納金(年会費)を払っています。

評価をよく見せるためにお金を払う会社は、きっと口コミも捏造している可能性もあります(もちろんそうじゃない店も多いでしょうが)

口コミもお金で捏造しているし、Twitterでたまに流れてくる「まじでこの世のすべての○○好きに教えてあげたいんだが」で始まるクソカスのゲボの宣伝マンにも50~100万円(2019/10現在の依頼価格)払って偽りの褒めをしてもらってバズらせている可能性が高いです。

 

一方食べログ3.6というのは、食べログから上納金を要求されて断った店が多いはずです。

なぜ上納金を断ったかはいろいろ理由があると思いますが、おそらく「年会費を払うくらいならお客さんに少しでも安く美味しいものを食べてもらいたい」という人間の優しい心からきているのではないでしょうか。

 

お店の人間ではないので詳しいことはわかりませんが、とにかく食べログ3.8以上の店よりは、食べログ3.6の店の方か美味しい可能性が高いと考えました。

  

やったこと

・食べログの評価3.60~3.69の店を取得する

・場所は東京都。理由は僕が東京在住なので。

 ※〇〇県もやって!という反応があれば随時対応していきます。(この記事にコメントor お問い合わせフォームから連絡 or TwitterにリプDM)

・ジャンルはラーメン(ラーメンが好きなので)。あと蕎麦(後輩から蕎麦もやれって脅されたので)

 

環境

・Python3.6

・BeautifulSoup4

・Mac

・うまいラーメンにありつきたいという強い意思 

 

結果発表

まずは気になっていると思うので、結果を貼り付けます。

GoogleMapの共有機能を使っています。

 

drive.google.com

 

皆さんの近くの店に食べログ3.6はあったでしょうか。多分美味しいのでぜひ行ってみてください。

あと、〇〇県でもやってくれ! ラーメン、蕎麦以外もやってくれ! ってのがありましたらコメントとかお願いします。

とりあえず記事は以上です。こっから下は、技術的な感じになるので気になる人は見てください。

 

食べログ3.6の作り方 

初Pythonでした。

食べログAPI、ずいぶん前に提供終了していて泣きました。何故なの。。。

しぶしぶスクレイピングしました。ソースは↓

表示する

 

Tabelog.py

import requests
from bs4 import BeautifulSoup
import re
import pandas as pd
import time

class Tabelog:
    """
    食べログスクレイピングクラス
    test_mode=Trueで動作させると、最初のページの3店舗のデータのみを取得できる
    """
    
    def __init__(self, base_url, test_mode=True, p_ward='東京', begin_page=1, end_page=50):

        # 変数宣言
        self.store_id = ''
        self.store_id_num = 0
        self.store_name = ''
        self.score = 0
        self.ward = p_ward
        self.review_cnt = 0
        self.review = ''
        self.columns = ['store_id', 'store_name', 'score', 'ward', 'review_cnt', 'review']
        self.df = pd.DataFrame(columns=self.columns)
        self.__regexcomp = re.compile(r'\n|\s') # \nは改行、\sは空白

        page_num = begin_page # 店舗一覧ページ番号

        if test_mode:
            list_url = base_url + str(page_num) +  '/'
            self.scrape_list(list_url, mode=test_mode)
        else:
            while True:
                    
                #ここでsleepを入れないと愛知県警岡崎署に威力業務妨害で逮捕されてしまう不具合が発生する!
                #そのため処理速度は落ちるが1秒スリープする
                #
                #■刑法234条■
                #威力を用いて人の業務を妨害した者は、3年以下の懲役又は50万円以下の罰金に処する。
                #(岡崎市立中央図書館事件:https://ja.wikipedia.org/wiki/%E5%B2%A1%E5%B4%8E%E5%B8%82%E7%AB%8B%E4%B8%AD%E5%A4%AE%E5%9B%B3%E6%9B%B8%E9%A4%A8%E4%BA%8B%E4%BB%B6)
                
                time.sleep(1)
                list_url = base_url + str(page_num) +  '/'
                if self.scrape_list(list_url, mode=test_mode) != True:
                    break

                # INパラメータまでのページ数データを取得する
                if page_num >= end_page:
                    break
                page_num += 1
        return

    def scrape_list(self, list_url, mode):
        """
        店舗一覧ページのパーシング
        """
        r = requests.get(list_url)
        if r.status_code != requests.codes.ok:
            return False

        soup = BeautifulSoup(r.content, 'lxml')
        soup_a_list = soup.find_all('a', class_='list-rst__rst-name-target') # 店名一覧

        if len(soup_a_list) == 0:
            return False

        if mode:
            for soup_a in soup_a_list[:2]:
                item_url = soup_a.get('href') # 店の個別ページURLを取得
                self.store_id_num += 1
                self.scrape_item(item_url, mode)
        else:
            for soup_a in soup_a_list:
                item_url = soup_a.get('href') # 店の個別ページURLを取得
                self.store_id_num += 1
                self.scrape_item(item_url, mode)

        return True

    def scrape_item(self, item_url, mode):
        """
        個別店舗情報ページのパーシング
        """
        start = time.time()
        r = requests.get(item_url)
        if r.status_code != requests.codes.ok:
            #print(f'error:not found{ item_url }')
            return

        soup = BeautifulSoup(r.content, 'html.parser')

        # 店舗名称取得
        # <h2 class="display-name">
        #     <span>
        #         麺匠 竹虎 新宿店
        #     </span>
        # </h2>
        store_name_tag = soup.find('h2', class_='display-name')
        store_name = store_name_tag.span.string
        #print('{}→店名:{}'.format(self.store_id_num, store_name.strip()), end='')
        self.store_name = store_name.strip()

        #店名を取得したい
        soup_address = soup.find("p", class_="rstinfo-table__address")
        #print(soup_address.get_text())

        # ラーメン屋、つけ麺屋以外の店舗は除外
        store_head = soup.find('div', class_='rdheader-subinfo') # 店舗情報のヘッダー枠データ取得
        store_head_list = store_head.find_all('dl')
        store_head_list = store_head_list[1].find_all('span')
        #print('ターゲット:', store_head_list[0].text)

#        if store_head_list[0].text not in {'ラーメン', 'つけ麺'}:
#            #print('  ラーメンorつけ麺のお店ではないので処理対象外')
#            self.store_id_num -= 1
#            return


        # 評価点数取得
        #<b class="c-rating__val rdheader-rating__score-val" rel="v:rating">
        #    <span class="rdheader-rating__score-val-dtl">3.58</span>
        #</b>
        rating_score_tag = soup.find('b', class_='c-rating__val')
        rating_score = rating_score_tag.span.string

        if rating_score == '-':
            #print('  評価がないため処理対象外')
            self.store_id_num -= 1
            return

        if float(rating_score) < 3.6 or float(rating_score) >= 3.7:
            #print('  3.6でないので対象外')
            self.store_id_num -= 1
            return
            
        #ターミナルに結果を出力させる
        print('{},{},'.format(self.store_id_num, store_name.strip()), end='')
        print('{},'.format(soup_address.get_text()), end='')
        print('{}'.format(rating_score), end='')
        print("\n")
        self.score = rating_score
        return 

#ラーメン:https://tabelog.com/tokyo/rstLst/ramen/
#そば:https://tabelog.com/tokyo/rstLst/soba/

Result = Tabelog(base_url="https://tabelog.com/tokyo/rstLst/ramen/",test_mode=False, p_ward='東京')

 

特に気をつけることはないです。ラーメンならラーメンのURL、蕎麦なら蕎麦のURLを入れてください。

ジャンル指定しないのも可能です。(URLは指定してください)

 

あえて気をつける点をあげるとすれば以下。

#ここでsleepを入れないと愛知県警岡崎署に威力業務妨害で逮捕されてしまう不具合が発生する!
#そのため処理速度は落ちるが1秒スリープする
#
#■刑法234条■
#威力を用いて人の業務を妨害した者は、3年以下の懲役又は50万円以下の罰金に処する。
#(岡崎市立中央図書館事件:https://ja.wikipedia.org/wiki/%E5%B2%A1%E5%B4%8E%E5%B8%82%E7%AB%8B%E4%B8%AD%E5%A4%AE%E5%9B%B3%E6%9B%B8%E9%A4%A8%E4%BA%8B%E4%BB%B6)
                
time.sleep(1) 
list_url = base_url + str(page_num) +  '/' 

 スクレイピングは、十分なスリープを入れていたとしても、有能な愛知県警岡崎署によって逮捕されてしまう場合があります(岡崎市立中央図書館時間)

 

スクレイピングとクラッキングの区別がついていない国家機関ですので、スクレイピングする際はくれぐれも自己責任でお願いします。

 

動かしてみた

1,鴨亭,東京都中央区築地4-2-11 新橋演舞場別館1F,3.61

2,玉椿,東京都台東区柳橋1-6-1 ,3.62

3,利き蕎麦 存ぶん,東京都目黒区中根1-6-1 ニューヨークコーナー161 2F,3.60

4,汐見,東京都新宿区早稲田鶴巻町556 松下ビル 1F,3.64

5,長生庵,東京都中央区築地4-14-1 モンテベルデ 1F,3.67

上記を実行するとこんな風にコンソールに出力されます。(左から、連番、店名、住所、食べログ評価)

CSV形式で吐かせてもよかったんですが、コンソール出力で十分なのでprintだけにしました。皆さんはお好きなようにカスタマイズしてください。 

 

まとめ

うまい店にありつきたい、という一心でpythonでスクレイピング頑張りました。

よかったら皆さんも地図を参考にうまい店にありついてくれれば幸いです。 

↑読者登録してくれると僕が舞い上がってニコニコします。