PythonによるWebスクレイピング + Amazon QuickSightで大黒天物産ダッシュボードを作る
データアナリティクス事業本部の貞松です。
Amazon QuickSightでは、地理空間グラフ(地図上にプロットした円の色や大きさにより、地理的な位置関係とそれにまつわる分類や数値を視覚化したもの)を利用することができます。自動ジオコーディング機能(地名や住所から自動で緯度・経度を取得してくれる機能)については、米国のみの対応となっていますが、データセットにあらかじめ緯度・経度の情報を含めておけば日本の地図に対しても地理空間グラフを使用できます。
AWSドキュメント - Amazon QuickSightユーザーガイド - 地理空間グラフ (マップ)
本記事では、この地理空間グラフを使った一例として、庶民の味方、大黒天物産の店舗ダッシュボードを作成します。
大黒天物産とは
大黒天物産株式会社は岡山県倉敷市に本社を置くディスカウントストア(ラ・ムー、ディオなど)の運営企業です。 プライベートブランドである「D-PRICE」の商品はちょっと引くぐらいの激安商品がラインナップされています。
ダッシュボードに必要なデータ
今回は、地理空間グラフを使うことを目的としているので、最低限店舗名と店舗所在地の緯度・経度が必要です。
また、大黒天物産の店舗は様々なブランド(ラ・ムー、ディオなど)があり、その違いによって営業時間や取り扱うサービスも異なるので、その辺りの情報も含めたいと思います。
Webスクレイピングで店舗情報を取得する
大黒天物産のWebページには、店舗検索のページがあり、こちらから各店舗の情報を得られそうです。
今回はPythonを使ってWebページをスクレイピングしつつ、必要な情報の追加や加工を行います。
PythonによるWebスクレイピング
PythonによるWebスクレイピングには BeautifulSoup4 を使用します。
pipでサクッとインストールして使用します。
1 | pip install beautifulsoup4 |
Seleniumを使って動的ページに対応する
大黒天物産の店舗検索ページは上の画像のように、javascriptで動的に店舗情報のリストを取得して表示するタイプのページなので、Seleniumを使って一番下までスクロールして店舗リストを表示させることを繰り返した上でページのソースを抜き出します。
GitHub - SeleniumHQ / selenium
これもpipでサクッとインストールします。
1 | pip install selenium |
Seleniumを実行する為に使用するブラウザに合わせたWebDriverが必要なので、あらかじめダウンロード、配置しておきます。
今回はChromeDriverを使用しました。
ChromeDriver - WebDriver for Chrome
geocoding.jpのAPIを使って住所から緯度・経度を取得する
QuickSightの地理空間グラフを使用する為には、住所から緯度・経度を取得しておく必要があります。
今回はgeocoding.jpのAPIを利用させていただきます。店舗検索のページから取得した住所を渡して緯度・経度を取得します。
Pythonのコードからgeocoding.jpのAPIを利用する方法については、下記のページを丸々参照しました。
住所から緯度経度を取得するpythonスニペット - Qiita
CSVファイルに出力する
取得したデータを加工しつつ、最終的には以下の列項目を持つデータをCSVファイルとして出力します。
- ブランド
- 店舗名
- 郵便番号
- 住所
- 緯度
- 経度
- 取扱サービス
- 取扱サービス数
作成したコードの全体像
作成したコードの最終的な状態は以下の通りです。
| import csv | |
| import requests | |
| import sys | |
| from bs4 import BeautifulSoup | |
| from selenium.webdriver import Chrome, ChromeOptions | |
| from selenium.webdriver.common.keys import Keys | |
| from time import sleep | |
| from tqdm import tqdm | |
| def get_lat_lon_from_address(address_l): | |
| """ | |
| address_lにlistの形で住所を入れてあげると、latlonsという入れ子上のリストで緯度経度のリストを返す関数。 | |
| >>>>get_lat_lon_from_address(['東京都文京区本郷7-3-1','東京都文京区湯島3丁目30−1']) | |
| [['35.712056', '139.762775'], ['35.707771', '139.768205']] | |
| """ | |
| url = 'http://www.geocoding.jp/api/' | |
| latlons = [] | |
| for address in tqdm(address_l): | |
| payload = {"v": 1.1, 'q': address} | |
| r = requests.get(url, params=payload) | |
| ret = BeautifulSoup(r.content,'lxml') | |
| if ret.find('error'): | |
| raise ValueError(f"Invalid address submitted. {address}") | |
| else: | |
| lat = ret.find('lat').string | |
| lon = ret.find('lng').string | |
| latlons.append([lat,lon]) | |
| sleep(10) | |
| return latlons | |
| # Selenium WebDriverの設定 | |
| options = ChromeOptions() | |
| options.add_argument('--headless') | |
| driver = Chrome(options=options, executable_path='./chromedriver') | |
| # コマンドライン引数としてエリア番号と都道府県コードの2つを渡す | |
| args = sys.argv | |
| # 引数の数をチェックする | |
| if len(args) == 3: | |
| # 店舗検索ページのURLを組み立てる | |
| url = 'http://www.dkt-s.com/area.html?reg={}&pref={}'.format(args[1], args[2]) | |
| driver.get(url) | |
| sleep(3) # ページ初期表示の読み込みを待つ | |
| # ページの下までスクロールして読み込みを待つ操作を繰り返す | |
| for i in range(5): | |
| driver.execute_script('window.scrollBy(0,5000)', '') | |
| sleep(5) | |
| html = driver.page_source.encode('utf-8') | |
| soup = BeautifulSoup(html, "html.parser") | |
| # 店舗名の取得 | |
| h2 = soup.find_all("h2") | |
| shop_name = [] | |
| for tag in h2: | |
| try: | |
| string_ = tag.get("class").pop(0) | |
| if string_ in "name": | |
| shop_name.append(tag.string) | |
| except: | |
| pass | |
| # 店舗名からブランドを取得する | |
| shop_brand = [] | |
| brand_list = ['ディオマート', 'ディオ', 'ら・む〜マート', 'ラ・ムー', 'ザ・大黒天'] | |
| for shop in shop_name: | |
| for brand in brand_list: | |
| if brand in shop: | |
| shop_brand.append(brand) | |
| break | |
| # 郵便番号と住所の取得 | |
| p = soup.find_all("p") | |
| shop_zipcode = [] | |
| shop_address = [] | |
| for tag in p: | |
| try: | |
| string_ = tag.get("class").pop(0) | |
| if string_ in "address": | |
| shop_zipcode.append(tag.string[:9]) | |
| shop_address.append(tag.string[10:]) | |
| except: | |
| pass | |
| # 住所から緯度・経度を取得 | |
| latlons = get_lat_lon_from_address(shop_address) | |
| # 店舗ごとの営業時間・取り扱いサービスを取得 | |
| p = soup.find_all("div") | |
| services = [] | |
| services_count = [] | |
| for div_tag in p: | |
| try: | |
| services_text = "" | |
| count = 0 | |
| string_ = div_tag.get("class").pop(0) | |
| if string_ == "service-icons": | |
| for li_tag in div_tag.select("ul li"): | |
| services_text += li_tag.string + " / " | |
| count += 1 | |
| services.append(services_text[:-3]) | |
| services_count.append(count) | |
| except: | |
| pass | |
| # CSVファイルに出力 | |
| with open('./output/daikokuten_shop_list_{}_{}.csv'.format(args[1], args[2]), 'w') as f: | |
| writer = csv.writer(f) | |
| writer.writerow(['ブランド', '店舗名', '郵便番号', '住所', '緯度', '経度', '取扱サービス', '取扱サービス数']) | |
| for brand, name, zipcode, address, latlon, service, count in zip(shop_brand, shop_name, shop_zipcode, shop_address, latlons, services, services_count): | |
| writer.writerow([brand, name, zipcode, address, latlon[0], latlon[1], service, count]) | |
| else: | |
| print("引数の数が違います。引数にはエリア番号と都道府県コードの2つを渡してください。") |
コードを実行してCSVファイルを出力する
ターミナルで以下のコマンドを実行してCSVファイルを出力します。とりあえず岡山県の店舗一覧を取得します。
1 2 | # エリアコード=3 (中国地方) 都道府県コード=33 (岡山県) を引数に渡して実行python daikokuten_bussan_shop_scraping.py 3 33 |
出力されたCSVファイルは以下のような内容になっています。
QuickSightでダッシュボードを作成する
上述で出力したCSVファイルをQuickSightに取り込んでダッシュボードを作成します。
CSVファイルを取り込んでデータセット作成する
QuickSightのトップページから New analysis → New data set → Upload a file の順番に進みます。
出力したCSVファイルをアップロードした後、以下の画面で Edit setting and prepare data を選択します。
地理空間グラフで使用するLatitude(緯度)とLongitude(経度)に該当する列項目を設定します。
設定が完了したら Save & visualize を選択してビジュアライズ画面に移行します。
ビジュアライズ画面でダッシュボードを作成する
地理空間グラフを作成する
ビジュアライズの画面で、左下の Visual types から Points on map を選択します。
グラフの設定として、Geospatialに緯度と経度、Sizeに取扱サービス数、Colorにブランドを設定します。
ら・む〜マートは岡山駅周辺の中心市街地に固まっていて、その他は郊外に点在しているのが一目で確認できます。
また、円の半径(Size)に取扱サービス数を設定しているので、ある程度各店舗の規模感も把握できます。
店舗情報の一覧を作成する
さらに店舗情報の一覧を追加する為に、左上のAddボタンをクリックして、Add visualを選択します。
地理空間グラフの下に領域が追加されるので、左下の Visual types から Table を選択します。
Valueの項目として以下を設定します。
- ブランド
- 店舗名
- 郵便番号
- 住所
- 取扱サービス
テーマを編集する
QuickSightは最近のアップデートによりダッシュボードのテーマを設定できるようなっています。
Amazon Web Services ブログ - Amazon QuickSight の新しい API とテーマ機能で、分析をさらに進化させる
せっかくなのでテーマを設定して、より大黒天物産っぽさを出しましょう。
左下のThemesタブを開いて、Create one…を選択します。
まずは、ダッシュボード全体に関わるカラーテーマを設定します。公式のWebページの配色に合わせて設定してみます。
次に、グラフの配色を選択します。大黒天物産の店舗ブランドのロゴに合わせて設定しました。
最終的なダッシュボード画面
テーマを適用した最終的なダッシュボード画面は以下のようになりました。
地理空間グラフと一覧表で情報がうまくまとまっています。
まとめ
PythonによるWebスクレイピングとQuickSightで大黒天物産ダッシュボードを作成してみました。
予め緯度・経度のデータさえ準備できれば、QuickSightの地理空間グラフを簡単に使えることが確認できました。
高機能な商用GISソフトのようにはいきませんが、手持ちのデータを簡単に地図上にプロットできるので、その他のデータと組み合わせてダッシュボードを充実させることができそうです。