はなたの日記

プログラミングとギターのコードについて書きます

ツタヤの在庫検索が使いづらいのでPythonでどうにかしてみた

※この記事のプログラムをGUIアプリにしてみました↓
hanatasdiary.hatenablog.com


突然ですが皆さん、TSUTAYAの在庫検索システムを利用したことがありますか?
レンタル・販売 在庫検索 - TSUTAYA 店舗情報

これはCDやDVDの在庫を、店舗を指定して調べることができるスグレモノです。
しかし1つだけ欠点があります。。。
このシステム、在庫がある店だけを絞り込むことができません。そのため、在庫がある店を探し出すには、全ての店のページを見る必要があります。
5店舗ぐらい調べてみるものの、どこも取扱していないから「この商品はレンタル終わったんだ…」って諦めることが多々あります。

というわけで今回は、在庫がある店の情報のみを抽出し、csvにするプログラムを書いてみました。

スクレイピングの前に

使用するライブラリ

必要なライブラリは以下の通りです。

from bs4 import BeautifulSoup
import requests
from selenium import webdriver
import time
import os
import csv

今回はスクレイピングしたいページが、JavaScriptレンダリングされていたので、seleniumを利用します。ブラウザはPhantomJSを使用しました。ただし、PhantomJSは更新が終了してしまい、現在使用が推奨されていないので、他のヘッドレスブラウザを使用したほうが良いと思います。

必要な商品情報

在庫の検索には、「商品ID」・「販売形式(レンタルCD、セルDVDなど)」・「都道府県ID」が必要になります。
商品の概要ページから「商品ID」と「販売形式」が分かります。下図の赤枠で囲ったように、URLに含まれています。

f:id:hanatasdiary:20190203193558p:plain
商品概要のページ

スクレイピングの対象ページ

上図の青枠を押すと、「商品ID」と「販売形式」が反映された検索ページが表示されます。
ここで都道府県を設定して「検索」を押すと、店舗一覧が出てきます。そしてURLの末尾が「~&adr=○○(数字)」となっています。この数字が「都道府県ID」です。例えば東京都は13です。

スクレイピングの流れ

①下図の店舗一覧ページにある店舗URLを全て取得

f:id:hanatasdiary:20190203194502p:plain
店舗一覧のページ
②店舗URLを開いて、真ん中らへんの「お店の在庫情報」から店名と在庫情報を抽出
③在庫情報が[○]か[×]だったらcsvに店名と在庫情報を書き込む
④店舗一覧ページを次のページに遷移させる
以上の①~④を繰り返します。今回は抽出する情報が少ないので、クローリングは行っていません。

コード

というわけで、全体のコードはこんな感じになります↓

from bs4 import BeautifulSoup
import requests
from selenium import webdriver
import time
import os
import csv


# グローバル変数
item_id = '005570850'
item_type = 'rental_cd'
prefecture_id = '13'
csv_save_dir = './csv/'
driver = webdriver.PhantomJS()


# 在庫情報を取得する
def get_zaiko_info(url):
    # urlを開いてsoupへ
    driver.get(url)
    driver.implicitly_wait(10)
    html = driver.page_source
    soup = BeautifulSoup(html,'html.parser')

    # 店の名前と在庫情報を取得
    shop_name = soup.find('h3',class_='green clearfix').find('a').string
    zaiko = soup.find('div',class_='state').find('span').string

    # 取扱がない場合0をreturn、取扱がある場合はcsvに店名と在庫情報を書き込む
    if '-' in zaiko:
        print('{0}では取扱していません'.format(shop_name))
        return 0
    else:
        with open(csv_save_dir+'zaiko.csv','a',newline='') as file:
            writer = csv.writer(file)
            writer.writerow([shop_name,zaiko])
        if '○' in zaiko:
            print('{0}では在庫があります'.format(shop_name))
        else:
            print('{0}では取扱していますが、現在在庫がありません'.format(shop_name))
        return 1


def main():
    # csvの保存場所作成
    os.makedirs(csv_save_dir,exist_ok=True)

    # 店一覧のページをsoupへ
    shop_search_url = 'https://store-tsutaya.tsite.jp/tsutaya/articleList?account=tsutaya&'\
                      'accmd=1&arg=https%3A%2F%2Fstore-tsutaya.tsite.jp%2Fitem%2F{0}%2F{1}'\
                      '.html&ftop=1&adr={2}'\
                      .format(item_type,item_id,prefecture_id)
    driver.get(shop_search_url)
    driver.implicitly_wait(10) # 更新待機
    html = driver.page_source
    soup = BeautifulSoup(html, 'html.parser')

    # 掲載ページ数を求める
    search_result_num = soup.find(class_='txt_k f_left').string
    total_shop_num = int(search_result_num.split('件')[1].split('全')[-1])
    if total_shop_num % 20 == 0:
        lastpage = total_shop_num / 20
    else:
        lastpage = int(total_shop_num/20) + 1

    cnt = 0 # 掲載されてる店舗数を数える

    # ページごと処理
    for page in range(1, lastpage+1):
        # 店舗一覧ページに戻る
        driver.get(shop_search_url)
        driver.implicitly_wait(10) # 更新待機

        # ページを更新
        driver.execute_script("ExecuteAjaxRequest('./articleList', 'account=tsutaya&accmd=1&"\
                              "arg=https%3a%2f%2fstore-tsutaya.tsite.jp%2fitem%2f{0}%2f{1}.html"\
                              "&searchType=True&adr={2}&pg={3}&pageSize=20&pageLimit=10000&"\
                              "template=Ctrl%2fDispListArticle_g12', 'DispListArticle'); return false;"\
                              .format(item_type,item_id,prefecture_id,page))
        driver.implicitly_wait(10)  # 更新待機
        time.sleep(5) # 更新待機

        # ページのsoupを取得する
        p_html = driver.page_source
        p_soup = BeautifulSoup(p_html, 'html.parser')

        # ページに掲載されている全店舗のURL取得
        table = p_soup.find('div', id='DispListArticle').find('table')
        links = table.find_all('a')
        for link in links:
            shop_url = link.get('href')
            cnt += get_zaiko_info(shop_url) # 在庫情報を取得

    print('{0}件の店舗で取扱しています'.format(cnt))

    driver.quit() # ブラウザを閉じる


if __name__ == '__main__':
    main()
実行結果

今回はflumpoolのEGGというアルバムをレンタルできる東京都内の店舗を探しました。
商品ID:005570850 販売形式:rental_cd 都道府県ID:13

f:id:hanatasdiary:20190204010523p:plain
店舗名と在庫状況が出力される
f:id:hanatasdiary:20190204010633p:plain
取扱している店舗はcsvに出力される

感想

seleniumは難しい」というイメージがあったが、意外と簡単に扱うことができた。練習を積んでいきたい。
個人的にはかなり便利なモノを作ったと思っているので、GUIアプリにしてみるなどして発展させていきたい。