LoginSignup
33
14

More than 1 year has passed since last update.

はじめに

こんにちは

Advent Calendar 2022

始まりましたね。私、初めての参加となり、非常にワクワクしとります。
まだ埋まってない日程があったりと不安はありますが、せっかくなのでみんなで楽しんで記事を書いて走り切りたい気持ちです。

では3日目、行きましょう!

この記事は TLB Enjoy Developers Advent Calendar 2022 の3日目の記事です。

さて今回の記事ですが、もう時が経つのも早いもので2022年も残り1ヶ月を切りました

師走といえばやっぱり忘年会

忘年会といえば飲み会

飲み会といえば...

ビールですよね?

image.png

私も東京に上京して早1年が経過しました。
上野に新宿、渋谷に赤羽、神田、新橋、北千住、品川、下北沢、巣鴨、etc...
ありがたいことにいろんな場所でお友達や同僚、いろんな方と飲ませていただきました。

上京するまでは大阪に住んでおりましたので、大阪でもいろんなところで飲み歩いておりました。

で、思ったことがあるんです。

東京の生ビール、高くないですか??

money_saifu_kara_man.png

大阪では大体300円台、安くて99円とか...あまり500円を超えることはなかったと思います。

それが上京してからは400円台は当たり前、高くて500〜600円もしばしば...

1回の飲み代もバカにならない...

大阪から上京した友人も東京での飲みは1回5〜6000円がデファクトスタンダードと認識しているようです。

ここいらでいっちょ東京の生ビール価格と大阪の生ビール価格
どれくらい違うのか検証したいと思ったのが今回の記事のテーマでございます。

目次

使用技術

  • Python 3.10.8
  • beautifulsoup4 4.11.1
  • requests 2.28.1

※細かいライブラリ系は省略します

検証方法

検証方法は至ってシンプルです。

グルメサイトに登録されているあらゆる店舗のドリンクメニューページにて「生ビール(生中)」の価格スクレイピングし、東京と大阪の生ビールそれぞれの平均値を算出することで、
いかに東京の生ビールが高いかをあらためて世間に訴えかけるわけです。

image.png

絞り込む要素として

  • 住所が東京都内、大阪府内であること
  • 飲食店のジャンルとしては居酒屋であること
    が最低限必須項目です。

ビールの詳細については実装しながら説明します!

対象サイトの選定

結論から今回はホットペッパーグルメさんを利用したいと思います。
理由は下記の通りです。

  1. ドリンクページのURLに統一性がある
  2. APIが提供されている

詳しくご説明いたします。

ドリンクページのURLに統一性がある

ぐるなび、食べログなども視野に入れておりましたが、ドリンクページのURLが統一されていたのはホットペッパーグルメでした。

ある飲食店のURLを引用します。
https://www.hotpepper.jp/strJ000960806/drink/

ドメインの後に、店舗IDらしきもの、
そしてdrink
ん〜〜実にシンプル、素晴らしい!

例えばぐるなびさんですと、
https://r.gnavi.co.jp/gajn110/menu4/
https://r.gnavi.co.jp/n187308/menu2/

サブディレクトリにmenu4やmenu2、drinkとバラエティに豊富でしたので、
ドリンクメニューページに辿り着くのにやや工数がかかりそうです。

APIが提供されている

これは残念ながらぐるなびや食べログは以前は提供していたそうですが、現在は廃止されているようです。

正直1番目の理由でホットペッパー一択なんですが、
ホットペッパーに関しては現在もAPIを提供していただいておりますので、もう確定です。
さすがリクルート。ありがとうございます。

こちらのリファレンスを参照すると、店舗を絞って店舗IDを取得できそうです。

スクリーンショット 2022-12-03 1.41.06.png

いかにスクレイピングするURLリストを素早く作っていくかが肝になるので、本当にありがたいですね。

利用規約

ここで一つ重要なポイント。

スクレイピングはサイトによっては利用規約違反となることもあります。
利用規約で明示的に「スクレイピングの禁止」と記載があればあきらめましょう。

幸いスクレイピングに関する記載はなさそうです。

引っかかる点とすれば第12条

【18】不正なプログラム・スクリプトなどを用いて、サーバーに負荷を与える行為

こちらはスクレイピング時にしっかりtime.sleep使って極力負荷をかけないように注意しましょう。

実装

API

早速APIをポチポチ叩いてみてみます。
叩き方の詳細はぜひリファレンスを確認してみてくださいね。

スクリーンショット 2022-12-03 1.50.12.png

レスポンスフィールドのこちらが飲食店を一意に取得できそうなIDです。

実際に東京居酒屋で絞り込んでみましょう。
スクリーンショット 2022-12-03 2.01.31.png

なるほど、10185軒の飲食店がヒットしました。
(result_availableがヒットした店舗数です。)
idをキーに店舗IDが取れているのがわかりますね。

では大阪居酒屋はどうでしょう。

スクリーンショット 2022-12-03 2.03.59.png

.......

スクリーンショット 2022-12-03 2.05.14.png

は?

早くも大阪と東京の絶大な格差を垣間見た気がします。
ここまで店舗数に差があるとは思ってませんでした。

URLリスト生成

気を取り直して
こちらのAPIをPythonで叩いて、URLリストを生成していきましょう。

for i in range(1, result_available, 100):
    params = {
        "key": "API KEY", # APIキー。取得方法はリファレンス参照
        "large_area": "Z011", # エリアコード:東京
        "genre": "G001", # 飲食店種別:居酒屋
        "format": "json",
        "count": 100, # 1ページあたりの店舗数
        "type": "lite", # レスポンスフィールドを少なめにする
        "start": i # 何番目の店舗から表示するか
    }
    p = urllib.parse.urlencode(params)
    url = 'http://webservice.recruit.co.jp/hotpepper/gourmet/v1/?' + p

ざっとこんな感じでURL生成のリストを生成していきます。
重要なのは

  • 東京都内・大阪府内であること
  • 居酒屋であること

1ページあたり最大100軒表示なので、先ほどAPIを叩いて得たresult_availableの値に達するまで表示する店舗を100軒ずつ回していきます。

drink_url=f'https://www.hotpepper.jp/str{shop_id}/drink/'

取得したJSONからidのみを取得してURLを生成してリスト化したテキストファイルを作成します。

ざっとこんな感じです。
スクリーンショット 2022-12-03 2.17.44.png

スクレイピング

メインディッシュです。

requestbeautifulsoupを用いて先ほど取得したURLにアクセスして、生ビールの価格を取得していきます。

まずはドリンクメニューのサイトを検証ツールを用いてよく観察して、どのタグ、どの要素、どのDOMを取得していくか探してみます。
スクリーンショット 2022-12-03 2.30.54.png

h4, class="firstChild", class="price"あたりがキーになってきそうです。

url = 'https://www.hotpepper.jp/strJ001017281/drink/'
html_text = requests.get(url).text
soup = BeautifulSoup(html_text, 'html.parser')

for h4 in soup.find_all('h4'):
    print(h4)

適当なページからh4タグのみ取得してみます。

<h4 class="firstChild">キリン一番搾り 生ビール</h4>
<h4 class="firstChild">瓶ビール(中瓶)</h4>
<h4 class="firstChild">【ノンアルコールビールテイスト飲料】キリン ゼロイチ</h4>
<h4 class="firstChild">ホッピーセット/黒ホッピーセット</h4>
<h4 class="firstChild">ジムビーム</h4>
<h4 class="firstChild">角瓶</h4>
<h4 class="firstChild">メーカーズマーク</h4>
<h4 class="firstChild">ジョニーウォーカー ゴールドラベル</h4>
<h4 class="firstChild">生ジンジャー/コーラ&amp;レモン/エルダーフラワー</h4>
<h4 class="firstChild">山梨産 白桃/長野産 シャインマスカット/馬路村のゆず・はちみつ</h4>
<h4 class="firstChild">シールド乳酸菌&生レモン/瀬戸内レモン</h4>

素晴らしい!!!
どうやらドリンクの名称のみがh4タグに割り当てられているようです。
ではそのh4タグのすぐ後ろのpタグがclass="price"であればそのドリンクの価格が取得できそうです!!

従ってh4タグの名前が「生ビール」であれば、その後のpタグ、class="price"を取得すれば、
その店舗の生ビールの価格が手に入るわけですね!!

と、そんなに甘くなく、店舗によって「生ビール」の表記がバラエティに富んでいました。

いくつか例を挙げましょう。
パターン1
スクリーンショット 2022-12-03 2.43.30.png

なるほど、「生ビール」ではなく銘柄を書くスタイルですね
てか店舗によって背景色とか変えれるんですね...

パターン2
スクリーンショット 2022-12-03 2.43.53.png

ピッチャーは想定外

パターン3
スクリーンショット 2022-12-03 2.44.44.png

商品名に価格をまとめるな

パターン4
スクリーンショット 2022-12-03 2.44.20.png

価格を最後にまとめるな
あと中便

パターン5
スクリーンショット 2022-12-03 2.43.13.png

少し飲みたい方は是非!とちゃいますねん

パターン6
スクリーンショット 2022-12-03 2.45.12.png

おいおいおいおいおいお


あくまでも私は生中!!ジョッキ!!発泡酒ではない生ビール!!!!の価格が知りたいんです。
色々試行錯誤を経て取得対象となるドリンクメニューに以下のような制約をかけました。

beer = '生ビール'
raw_beer = '生中'
premium = 'プレミアムモルツ'
premium1 = 'プレミアム・モルツ'
super_dry = 'スーパードライ'
malts = 'ザ・モルツ'
yebisu = 'YEBISU'
yebisu1 = 'エビス'
kirin = '一番搾り'
sapporo = 'サッポロ'

こちらは取得対象となるh4タグのキーワードです。
生ビール、生中をベースにホットペッパーでよく見かけたビールの銘柄をいくつかピックアップしました。
(ちなみに私の好きな銘柄はエビスです。)

self = 'セルフ'
mega = 'メガ'
king = 'キング'
pitcher = 'ピッチャー'
big = ''
mini = ''
glass = 'グラス'
mouse = '一口'
vase = ''
vase_kana = 'びん'
baka = 'バカ'

こちらは除外キーワードになります。
「生ビール」と記載があっても「セルフサービスでこの値段!」「中瓶」「ピッチャー」「メガ生ビール」「生大」といったような下位互換な(人によっては上位互換かもしれない)ワードがありましたので省きます。
これらは価格を無闇に吊り上げたり、あるいは引き下げたりする要素となりますので危険です。

「バカ」はさすがに少数派ですが、偶然見つけてしまったので省きます。
スクリーンショット 2022-12-03 3.02.46.png

あと、クラフトビールもありましたが、さすがにここまで細かく絞っていくとキリがないので諦めます。
おそらく大阪も東京も一定数店舗があると思いますので、ここは算出の対象に含めちゃいましょう。

スクリーンショット 2022-12-03 2.45.53.png

取得結果がこちらです

スクリーンショット 2022-12-03 3.07.07.png

こんな感じで、店舗IDと生ビール価格をJSONで保存することができました。

これでもやはり、

  • URLの有効期限切れ(掲載終了など)
  • ビール価格が掲載されていない店舗 etc...

といった理由で価格が取れていない店舗がいくつかありました。

しかしこれでもある程度十分量取得できましたので今回はヨシとしましょう。
image.png

解析

先ほど生成したJSONを読み込んで、解析していきましょう!

価格は文字列として保管されております。
ここを数値だけにの価格リストを生成し、最終的に平均値を算出する流れです。

price_list = []
exist_beer_shop = 0
for price_str_list in price_data.values():
    if not price_str_list:
        continue
    exist_beer_shop += 1
    for price_str in price_str_list:
        prices = re.findall(r"\d+", price_str.replace(',', ''))
        for price in prices:
            if int(price) > beer_max_price:
                continue
            price_list.append(int(price))

とりあえずざっとこんな感じです。
price_dataが先ほどのJSONを読み込んだデータですね。

re.findall(r"\d+", price_str.replace(',', ''))

ここの部分ですが、価格文字列から数値(価格)だけを抽出するに当たって、
1,000円とか2,240円とか1000円を超えるとカンマ区切りになり、
それぞれが別の数値としてリストに保存されてしまうので、あらかじめカンマは消しておきます。
([2, 240, ...]みたいな)

if int(price) > beer_max_price:

ここは先ほど取得したJSONでもやはり高額な箇所がいくつかありました。
しかしサイトを見ると、ビッグサイズなビールであったり、飲み放題価格であったりしましたので、
今回は1500円を超えると一般的な生中と見做さないようにしておきます。

結果

お待たせしました。
ようやく結果発表です。

東京の生ビール価格

生ビールが存在した店舗数 : 7111
生ビールリストの要素数 : 8998
生ビールの平均値 : 547.627695043343
生ビールの最安値 : 110

生ビール価格が取得できたのは10185軒中7111軒
その中で生ビールに関する価格を取得できた数は8998ビール

平均価格は547.6円!

高いですね...

最安値110円!!??
いくらなんでも安すぎでは...??

いやいや虎ノ門でこの価格は絶対無理でs...

スクリーンショット 2022-12-03 3.45.11.png

...やばそう

大阪の生ビール価格

さぁ、我が故郷よ、その真価を発揮してくれ

生ビールが存在した店舗数 : 3181
生ビールリストの要素数 : 4033
生ビールの平均値 : 483.3994545003719
生ビールの最安値 : 0

生ビール価格が取得できたのは4696軒中3181軒
その中で生ビールに関する価格を取得できた数は4033ビール

平均価格は483.3円!

んーー・・・
思ってたんとちょっとちゃいますね。
もっと差が開くかと思いきや、その差僅かおよそ60円・・・。

いやそれよりも
最安値0円??

天満ならやってくれそうな気がs...

スクリーンショット 2022-12-03 3.52.55.png

あっ....(察し

おわりに

年末は天満で飲むことにしました


明日は@KeiFunahashiさんの記事です!!
乞うご期待を!!

33
14
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
33
14