77Lifeworkベータ版

77Lifeworkベータ版

IT関係の話(ツール開発・インフラ構築)をメインとして、その他私の趣味や雑記用のブログです。ここに書いた内容が少しでも参考になれば嬉しいです。

Pythonによるスクレイピングで炭焼きさわやかに待たずに入れる時間帯を調べる

こんにちは。タイトルが意味不明かもしれませんが、これは以下の記事の続き的な立ち位置です。
www.77-lifework.com

さわやかの待ち時間を調べられるサイトを定期的にスクレイピングし、待ち時間をスプレッドシートに記録した上、どの時間帯が空いているのか調べてみようというテーマです。
動機や背景は前の記事と同じなので、はしょります。
割と真剣にやっちゃいましたので、ぜひご覧ください。

処理の流れと出力結果

まず、使用しているプログラミング言語Pythonで、待ち時間をスクレイピングし、Googleスプレッドシートに結果を書き込むプログラムを作成しました。これをLinuxサーバに配置し、cronで15分ごとに実行しています。
結果は以下のようになります。日付ごとのシートが作成され、各店舗の待ち時間が書き込まれています。
f:id:J-back:20200126001754p:plain:w700

営業時間外の場合は「-1」を書き込むようにしています。データベースで管理するほど大量のデータを扱う気はなかったので、今回は代わりにスプレッドシートを使用しました。

Pythonのコード

さて、今回のPythonコードですが、以下です。

# coding: UTF-8
import os
import locale
import gspread
from oauth2client.service_account import ServiceAccountCredentials
import slackweb
import time
import datetime
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

# 定数宣言
SLACKURL_ex = '例外発生時のslack通知用URLを入力'

#jsonファイルを指定
CREDENTIAL_JSON = 'スプレッドシートアクセスのためのjsonファイルのパスを指定'

# main処理
def main():

    scope = ['https://spreadsheets.google.com/feeds',
            'https://www.googleapis.com/auth/drive']

    # 認証
    credentials = ServiceAccountCredentials.from_json_keyfile_name(CREDENTIAL_JSON, scope)
    gc = gspread.authorize(credentials)

    # 対象ブックを指定
    target_book = gc.open('sawayaka_analysis')

    # 現在日付を取得
    now_date = datetime.date.today().strftime('%Y-%m%d')

    # シート名一覧を取得
    worksheet_list = target_book.worksheets()
    sheetname_list = []
    for wsList in worksheet_list:
        sheetname_list.append(wsList.title)

    if not now_date in sheetname_list :
        target_book.add_worksheet(title=now_date, rows=100, cols=26)
    
    # 書き込み対象のシートを指定
    target_sheet = target_book.worksheet(now_date)

    # スクレイピング対象URL
    URL = "https://www.genkotsu-hb.com/airwait.php"

    # スクレイピング実行
    try:

        # 現在時刻取得
        now_time = datetime.datetime.now().strftime('%H:%M')       

        # Chrome設定
        options = Options()
        options.add_argument('--headless')

        # Chromeを起動してurlを開く
        driver = webdriver.Chrome(options=options)
        driver.get(URL)
        time.sleep(20)

        # 文字コードをUTF-8に変換し、html取得
        html = driver.page_source.encode('utf-8')
        soup = BeautifulSoup(html, "html.parser")

        # 対象店名読み込み
        storeTagList = soup.find_all('div', class_='sc-fONwsr bmOhpB')

        # 店名入力対象セルを取得
        storeName_cells = target_sheet.range(1,1,1,len(storeTagList)+1)
        storeName_cells[0].value = '時刻/待ち時間' 

        for i in range(0, len(storeTagList)):
            storeName_cells[i+1].value = str(storeTagList[i]).replace('<div class="sc-fONwsr bmOhpB">', '').replace('</div>', '')

        # スプレッドシートへ店名書き込み
        target_sheet.update_cells(storeName_cells)

        # 待ち時間入力対象列を決定
        colList = target_sheet.col_values(1)
        target_rownum = len(colList) + 1

        # 待ち時間入力対象セルを取得
        waitTime_cells = target_sheet.range(target_rownum,1,target_rownum,len(storeTagList)+1)
        waitTime_cells[0].value = now_time

        for i in range(0, len(storeTagList)):
            # tagとclassを指定して要素を取り出す
            storeElement = soup.find('div', string=storeName_cells[i+1].value)
            waitingTimeObj = storeElement.find_parent('a').find('div',class_='sc-ipXKqB iHAIGD')

            if (waitingTimeObj is not None):
                waitTime_cells[i+1].value = waitingTimeObj.text.replace("約","").replace("分待ち", "")
            else:
                # 受付時間外は-1を代入
                waitTime_cells[i+1].value = -1

        # スプレッドシートへ待ち時間書き込み        
        target_sheet.update_cells(waitTime_cells)
            
    except Exception as e:
        message = "[例外発生]"+ os.path.basename(__file__) +"\n"+"type:{0}".format(type(e))+"\n"+"args:{0}".format(e.args)
        slackweb.Slack(url = SLACKURL_ex).notify(text = message)

    finally:
        # 起動したChromeを閉じる
        driver.close()
        driver.quit()


if __name__ == '__main__':
    main()

スプレッドシートへの書き込み処理については以下の記事を参考にしてください。
www.77-lifework.com


ちなみに、

# 定数宣言
SLACKURL_ex = '例外発生時のslack通知用URLを入力'

ってところと

   except Exception as e:
        message = "[例外発生]"+ os.path.basename(__file__) +"\n"+"type:{0}".format(type(e))+"\n"+"args:{0}".format(e.args)
        slackweb.Slack(url = SLACKURL_ex).notify(text = message)

ってところはなくても大丈夫です。
不測の事態が起きた時に通知されてるといいなー、と思ってslackに通知する処理を入れているだけなので、本プログラムの動作には関係ない部分です。

このコードを毎日15分ごとに実行した結果がさっき貼ったスプレッドシートの画像ですね。とりあえず全店舗について取得していますが、ある程度しぼっても良いかな、とも思います。

データ分析

分析、というほどすごいことはできないのですが(というか、スクレイピングできたところで結構満足してしまっています)、せっかくデータ取得したので、少し中身を見てみましょう。
Pythonコードを実行して作成したスプレッドシートのデータを使って、ある1週間について「御殿場インター店」の空き具合を曜日ごとにまとめたものが以下です。
f:id:J-back:20200126003412p:plain:w700

これをグラフにしてみると、以下のようになります。
平日に比べて土日のほうが全体的な待ち時間が多いのが見て取れますね、これは感覚的にも正しい結果です。
また、日曜日は基本的にどの時間帯も混んでいますが、土曜日だったら16:00〜17:30ぐらいが少し空いていそうな感じですね。
とはいえ、今回はデータが7日間分しかないので、たまたまこの土曜日だけこの傾向となっているのかもしれません。
もっと長い期間で曜日ごとに平均を出したりすれば、より現実に近い傾向がつかめるのではないでしょうか。
f:id:J-back:20200126003954p:plain:w700

最後に

今回はさわやかの待ち時間データを取得して分析するところまでやってみました。今回はさわやかの待ち時間というテーマでやりましたが、こんな感じで様々なデータを集めて分析してみるとおもしろいですね、アイデア次第でいろいろ応用がききそうです。
最後まで読んでくださり、ありがとうございました。