データ収集作業を自動化しよう

# はじめに
最近Lancersというアウトソージングサイトを見ていたら、データ入力や調査系の仕事が2円/件くらいでたくさんあることに気が付きました。しかも仕事依頼者は手作業で進めることを前提としており、応募している方々も皆そのように進めようとしていました。
これGoogle先生のAPI使えば無双できるんじゃね。ってのがこの記事を書いたモチベです。

Qiitaに集いしエンジニアの方々からすれば大したことないコードだと思いますが、このようなデータ収集等をいまだに手作業で進めている人類の方は多いと思いますので、そのような方でかつプログラミングに興味のある方を対象とした記事です。

デジタル庁も発足し、ITに疎いお父さんの口からもDXという言葉がもれる時代ですから、是非この機会に自動化をはじめてみませんか。

# 技術
- Python 
- Google Places API

# 作るもの
今回はLancersに落ちていた、「場所＋施設」で検索をかけて、その施設や店舗の名前・HPのURL・電話番号をエクセルに入力する仕事を自動化します。PythonとGoogle APIで情報を集めてCSVに出力しましょう。CSVにすればGoogleスプレッドシートやエクセルにもインポートできるので便利です。

# コード解説
### Google API
まずコードを書き始める前にGoogle Cloud Platformにプロジェクトを作ったりAPI認証用のキーを取得したりしないといけないので、「Google Mpas API はじめかた」や「Google Places API はじめかた」などと調べてAPI KEYの取得まで完了させてください。

また今回はPlaces APIの中でもPlaces Search APIとPlaces Detail APIを使います。Places Search APIはさらに検索の仕方で３種類に分かれていますが、Text Search APIを使います。

Text Search API：https://developers.google.com/maps/documentation/places/web-service/search-text
Places Detail API：https://developers.google.com/maps/documentation/places/web-service/details

### Pythonライブラリ
主要な役割をするのはrequestsとcsvというライブラリです。csvは標準ライブラリですが、requestsは違うのでpip等でインストールしてください。

### 簡単に解説
1. Text Search APIでキーワードを検索し、結果をsearch_resultに格納。ちなみにレスポンスデータが長い場合は複数ページ（API?）に分割され、next_page_tokenというのも返されるのでそれをもとに次のAPIを再帰的に呼び出しています。(access_textsearch関数)
1. Text Search APIで得たPlaceデータの配列でループをかけて、place_idを元にPlace Detail APIを呼び出す。websiteとformatted_phone_numberを各Placeデータに追加する。これらがない場合は空文字を入れる。(access_placedetail関数)
1. 「場所 施設.csv」という名前のCSVファイルを作成し、search_resultでループをかけて、各行にPlaceデータを出力していく。 （write_csv関数）

```python
import requests
import time
import csv

SEARCH_QUERY = '三宮 ジム'
API_KEY = '自分のAPI KEYを入れてください'

search_result = []

textsearch_url = 'https://maps.googleapis.com/maps/api/place/textsearch/json'
textsearch_q = {'query': SEARCH_QUERY, 'language': 'ja', 'key': API_KEY}

FIELDS = 'website,formatted_phone_number'
plcadetail_url = 'https://maps.googleapis.com/maps/api/place/details/json'
plcadetail_q = {'key': API_KEY, 'fields': FIELDS}

def main():
    access_textsearch(textsearch_url, textsearch_q)

    for p in search_result:
        time.sleep(2)
        website, phone_number = access_placedetail(plcadetail_url, plcadetail_q, p['place_id'])
        p['website'] = website
        p['phone_number'] = phone_number
    
    write_csv()

def access_textsearch(url , q, nexttoken=0):
    if nexttoken:
        q['pagetoken'] = nexttoken

    s = requests.Session()
    r = s.get(url, params=q)
    json_o = r.json()

    status = check_status(json_o['status'])
    if status != 'OK':
        print(status)
        exit()
    
    for r in json_o['results']:
        search_result.append({'place_id': r['place_id'], 'name': r['name']})
    
    if 'next_page_token' in json_o:
        time.sleep(2)
        access_textsearch(url, q, json_o['next_page_token'])


def access_placedetail(url, q, place_id):
    q['place_id'] = place_id
    s = requests.Session()
    r = s.get(url, params=q)
    json_o = r.json()
    
    status = check_status(json_o['status'])
    if status != 'OK':
        print(status)
        exit()

    if 'website' in json_o['result']:
        website = json_o['result']['website']
    else:
        website = ''
    if 'formatted_phone_number' in json_o['result']:
        phone_number = json_o['result']['formatted_phone_number']
    else:
        phone_number = ''
    return website, phone_number


def write_csv():
    with open(f'data/{SEARCH_QUERY}.csv', 'w', newline='') as f:
        writer = csv.writer(f)
        writer.writerow(["名前", "URL", "電話番号"])
        for p in search_result:
            writer.writerow([p['name'], p['website'], p['phone_number']])

def check_status(status):
    if status == 'ZERO_RESULTS':
        return '検索結果が0件です'
    elif status == 'INVALID_REQUEST':
        return '無効な(奇形な)リクエストです'
    elif status == 'OVER_QUERY_LIMIT':
        return 'リクエストの利用制限回数を超えました'
    elif status == 'REQUEST_DENIED':
        return 'リクエストが拒否されました(KEYやクエリに間違いがある)'
    elif status == 'UNKNOWN_ERROR':
        return '予期せぬエラーが発生しました'
    else:
        return 'OK'


if __name__ == "__main__":
    main()
```

# 結果
ちなみにこんな感じのCSVが完成します。たったの1分足らずです。
```csv
名前,URL,電話番号
東急スポーツオアシス三宮24Plus,https://www.sportsoasis.co.jp/sh13/?utm_source=google&utm_medium=mybusiness,078-333-8109
コナミスポーツクラブ 三宮,https://information.konamisportsclub.jp/ksc/004090/,078-291-6641
エニタイムフィットネス 三宮旭通店,http://www.anytimefitness.co.jp/sannomiyaa/,078-200-5701
ジムスタイルプラス三宮,http://www.gymstyle24.com/plus/,078-599-8981
ジョイフィット三宮,https://joyfit.jp/sannomiya/,078-381-8730
【神戸初！女性専用】24時間フィットネスジムＶＮＳＣＯＡ（ ヴィーナスコア）,https://vnscoa.com/,078-331-9555
FEELCYCLE 三ノ宮,https://www.feelcycle.com/,0570-055-319
GRANDSTONE神戸三宮店,http://grandstone.jp/,078-232-7086
エクササイズコーチ三宮店,https://exercisecoach.co.jp/store/kinki/sannomiya/,078-381-9700
jump one Sannomiya トランポリン×暗闇フィットネス,https://www.jumpone.jp/,078-599-8062
24/7ワークアウト 三宮店,https://247-workout.jp/gym/h_sannomiya.html?acckey=gym0-googmybu-0000-2470-glpforms-00000000&utm_source=google&utm_medium=gmb&utm_campaign=h_sannomiya,0120-501-247
Flexible Style神戸三宮店 ダイエットと姿勢改善のための女性専用パーソナルトレーニングジム,http://www.flexible-style.com/,050-5328-3808
UNDEUX（アンドゥ）神戸 パーソナルトレーニングジム,https://www.diet-undeux.jp/?utm_source=google&utm_medium=maps&utm_campaign=map_kobe,078-855-3898
ゴールドジム 神戸元町,https://www.goldsgym.jp/shop/28110,078-334-3434
テトラフィット ホワイト 神戸三宮店,https://tetra.fit/studio/kobesannomiya,078-862-3857
女性専用フィットネスミットネス 神戸店,https://mittness.jp/shop-kobe/?utm_source=google&utm_medium=map&utm_campaign=kobe,078-391-0787
三宮 パーソナルジム｜BEZELオアシス神戸三宮店,https://www.kaatsu-studio.net/studio/studio-kobe-sannomiya/,078-392-7370
フロージム 神戸三宮店｜パーソナルトレーニング,https://flow-gym.net/,080-6172-9604
アライブジム,http://alive-gym.com/,090-9881-5548
身体工房LIBERTEX／リベルテクス 神戸三宮店,https://www.libertex.training/,078-599-6204
DoDoPep,https://dodopep-diet.com/,090-7095-4594
Re:ly Fitness（リリーフィットネス） 三宮店,https://relyfitness.com/,078-891-7715
Surf Fit Studio（サーフフィット）神戸三宮店,https://surffit.jp/studio/kobe-sannomiya/?utm_source=Google_mybusiness&utm_medium=link&utm_campaign=kobe,0570-055-302
Revive〈リバイブ〉パーソナルジム神戸三宮,https://revivegym.web.fc2.com/,090-5155-2234
Private gym CONCIERGE,,090-1593-1112
Trust(トラスト) | プライベートジム神戸三宮,https://trust-gym.com/,070-8334-5752
アガーラ 三宮メディカルフィットネス,https://www.aga-la.com/,078-251-4444
JOYFIT24新神戸,https://joyfit.jp/shinkobe/,078-862-1576
タンニングセンター,,
Y-STYLE,http://yskobe.jp/,090-6988-8781
美脚専門プライベートジム IVY,https://www.ivy-gym.com/,078-855-8667
キックボクシングスタジオ リフィナス神戸三宮,http://refinas.jp/,06-6641-1588
フリーラウンド,https://www.freeround.jp/,078-335-5610
Re:ly Fitness（リリーフィットネス） 元町店,https://relyfitness.com/,078-325-1616
KARADA BESTA 神戸三宮店,https://besta.tokyo/,
Mahalo Training Gym,,078-381-7924
BEFORT,https://befort.jp/,078-381-9373
BUKATSU,https://bukatsu-gym.com/,078-806-8693
fullness,http://waistshapeup.com/,080-4390-9334
FiTNESS UNIVERSITY 神戸元町,https://www.fituni.jp/,080-3276-2596
personal training gym ONE,https://personalgymone.com/,090-4284-8286
iPTOxGym,http://iptoxgym.com/,078-862-5772
シェイプスガール（ShapesGirl）神戸三宮店,https://www.shapesgirl.com/kobesannomiya.html,078-335-5577
リスタイリッシュ神戸店,https://restylish.net/,078-599-6575
TopWorks-Body神戸元町店,https://topworks-body.com/kobemotomachi/?utm_source=mybusiness&utm_medium=referral&utm_campaign=mybusiness_kobemotomachi,078-599-9720
富永パーク,,
グラビティリサーチ ミント神戸,http://blog2.kojitusanso.jp/grmintkobe/,080-9706-3216
Lifelong(ライフロング),https://www.lifelong-conditioning.com/,080-9746-8156
オージースポーツ神戸福祉スポーツセンター,https://www.fukushi-sc.net/,078-271-5332
神戸三宮パーソナルトレーニング加圧筋調整,https://trainingconditioningkobe.wordpress.com/,
プロジム 神戸校,,
カーブス 神戸元町,https://www.curves.co.jp/gmb/?s_id=91024,078-325-5557
corpus(コアパス),,090-8501-0691
Joy Fit 24,http://www.joyfit.jp/,
BoostGym,https://boostgym.business.site/?utm_source=gmb&utm_medium=referral,090-4293-1399
FITNESS GYM WEST,https://fitnessgym-west.com/,078-381-9336
Blue Ocean Gym 神戸三宮店,https://www.blueocean-gym.com/,
RICHBODY,https://tatezaki-rb.com/,070-2288-4408
BRAVE FITNESS - ブレイブフィットネス,https://bravefitness.jp/,078-515-6185
UNIVERSAL JUMPING（ユニバーサルジャンピング）,https://universal-jumping.com/,078-855-7598
```

自動化で生産性を高めていきましょ♪