指定した都道府県にあるコンビニの住所を取得し、Google Mapで表示してみた[beautifulsoup4編]


はじめに

  • こんにちは ! KDDI アジャイル開発センターの小板橋です。
    今回は、scrapyを利用した面白いスクレイピングツールを拝見したため、僕はそれをbeautifulsoup4で再現してみました。

  • 内容としては、beautifulsoup4を利用し、NAVITIMEをスクレイピングし、指定した都道府県のコンビニ(セブンイレブン、ローソン、ファミリーマート)の住所を取得し、GCPのgeocoding apiを利用し緯度経度に変換後CSV出力するといったものです。

beautifulsoup4とは

beautifulsoup4とは、HTMLやXMLから欲しいデータを抽出するためのPythonライブラリです。

インストール方法
pipでインストールできます。

 pip install beautifulsoup4

BeautifulSoupでHTMLの中からHTML要素を取得するには、下記の2タイプのメソッドを使用します。

  • find系 find_all()、find()
  • select系 select()、select_one()

今回は、find系のfind_all()を使用しました。

ちなみに、上記のfind系とselect系の違いなのですが、

種類 すべての要素をリストで返却 ひとつだけ要素を返却 引数
find系 find_all() find() 要素名,属性指定
select系 select() select_one() CSSセレクタ

geocoding apiとは

住所を地理座標(緯度/経度)に変換できるAPIです。

インストール方法
pipでインストールできます。

pip install googlemaps

実装

注意点: For文の最大値を下記のコードでは東京都のセブンイレブンを検索時の最大ページ数にしている
→ 引数で得たコンビニを検索時のページ最大数を取得する修正をしようとして、力つきました。笑

google_map_scraping.py

#!/usr/bin/env python
from urllib import request, error
from bs4 import BeautifulSoup
import csv
import googlemaps
import time
import sys
from argparse import ArgumentParser, FileType

storepath = '0201001001'
citypath = '13'

def gettext():
    response = request.urlopen(url)

    try:
        with request.urlopen(url) as res:
            res.read()
    except error.HTTPError as err:
        print(err.code, "wwwww")

    soup = BeautifulSoup(response, 'html.parser')
    response.close()
    csvRow = []

    googleapikey = '個人のGoogelMapのAPIkeyを設定'
    gmaps = googlemaps.Client(key=googleapikey)

    for i in soup.find_all(class_="ellipsis"):
        i.extract()

    for j in soup.find_all(class_="spot-detail-value-text"):
            texts = j.get_text()
            csvRow.append(texts.strip())

    with open("store_lat_lng.csv", "a", encoding='utf-8') as file:
        for csvRows in csvRow:
            result = gmaps.geocode([csvRows])
            if len(result) != 0 :
                lat = result[0]["geometry"]["location"]["lat"]
                lng = result[0]["geometry"]["location"]["lng"]
                writer = csv.writer(file)
                writer.writerow([csvRows, lat, lng])
                time.sleep(0.5)
    print("終了")
    sys.exit()

p = ArgumentParser(description='指定した都市におけるコンビニの緯度経度をCSVとして吐き出す')
p.add_argument('--store', choices=['セブンイレブン', 'ローソン', 'ファミリマート'], default='セブンイレブン', help='default=セブンイレブン')
p.add_argument('--city', choices=['tokyo', 'Kanagawa', 'Osaka'], default='tokyo', help='default=tokyo')
args = p.parse_args()
if args.store == 'セブンイレブン' :
    storepath = '0201001001'
if args.store == 'ローソン' :
    storepath = '0201001009'
if args.store == 'ファミリマート' :
    storepath = '0201001006'
if args.city == 'tokyo' :
    citypath = '13'
if args.city == 'Kanagawa' :
    citypath = '14'
if args.city == 'Osaka' :
    citypath = '27'


url = 'https://www.navitime.co.jp/category/{0}/{1}'.format(storepath, citypath)
gettext()
for i in range(2, 60):
    url = 'https://www.navitime.co.jp/category/{0}/{1}/?page={2}'.format(storepath, citypath, i)
    gettext()

動作

--helpで引数を確認

~/Desktop
❯ ./遊び用Pythonツール/google_map_scraping.py --help
usage: google_map_scraping.py [-h] [--store {セブンイレブン,ローソン,ファミリマート}]
                              [--city {tokyo,Kanagawa,Osaka}]

指定した都市におけるコンビニの緯度経度をCSVとして吐き出す

optional arguments:
  -h, --help            show this help message and exit
  --store {セブンイレブン,ローソン,ファミリマート}
                        default=セブンイレブン
  --city {tokyo,Kanagawa,Osaka}
                        default=tokyo

~/Desktop
❯

Defaultの値で実行

~/Desktop
❯ ./遊び用Pythonツール/google_map_scraping.py
終了

CSVを確認

store_lat_lng.csv
東京都三鷹市新川6-3-14,35.68321359999999,139.5685815
東京都千代田区永田町2丁目2番1号,35.6745788,139.743009
東京都港区六本木3-2-1,35.6645869,139.7377776
東京都八王子市高尾町1806-1,35.6333012,139.2562665
東京都世田谷区玉川3-11-7,35.6136912,139.6256771
東京都世田谷区太子堂2-12-1,35.6450583,139.6735356
東京都大田区東海3丁目2番1号,35.5789543,139.7591679
東京都目黒区下目黒5-18-21,35.6306158,139.7021458
東京都町田市鶴間8丁目15番21号,35.5103597,139.4769608
東京都千代田区九段南3-1-1,35.692245,139.7434825
東京都福生市熊川1411-6,35.7253801,139.3431771
東京都中央区新川2-27-2,35.6742508,139.7843206
東京都中央区日本橋箱崎町22-1,35.6815297,139.7862889
東京都新宿区中落合3-1-3,35.7223184,139.6919351
東京都新宿区新宿2-6-4,35.6900244,139.7071167
東京都新宿区西新宿6-5-1,35.6931766,139.6931655
東京都八王子市石川町2441-5,35.6739094,139.363978
東京都八王子市戸吹町1469-1,35.70595,139.2971229
・
・
・

Google Mapで表示してみた

1.Google Mapで[マイプレイス]を選択します。

2.[地図を作成] → [インポート]を選択し、先ほどのPythonツールで出力したCSVを読み込ませる。
その後、列の緯度/経度をしていると一括で、表示される

多いですね。。w

おわり

注意点としては、geocoding apiの使用料ですかね、1000リクエストで$5なので、お金のない僕としてはきついですw