著者:川嶋 宏彰
最近、プログラミング言語「Python」による自動化やデータ分析が注目されています。本特集では、Pythonと、Webブラウザを自動操作するためのライブラリ「Selenium WebDriver」を用いて、インターネットから取得できるオープンデータを例に、Webブラウザの自動操作方法およびデータ分析方法を分かりやすく紹介します。
シェルスクリプトマガジン Vol.67は以下のリンク先でご購入できます。![]()
![]()
図4 非headlessモードのサンプルコード(sample.py)
import time
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://www.google.com/')
time.sleep(5)
search_box = driver.find_element_by_name('q')
search_box.send_keys('ChromeDriver')
search_box.submit()
time.sleep(5)
driver.quit()
図7 headlessモードのサンプルコード
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
driver = webdriver.Chrome(options=options)
driver.get('https://www.google.com/')
print(driver.title)
search_box = driver.find_element_by_name('q')
search_box.send_keys('ChromeDriver')
search_box.submit()
print(driver.title)
driver.save_screenshot('search_results.png')
driver.quit()
図24 Seleniumを用いた気温データの自動取得プログラム
import time
from pathlib import Path
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
# ダウンロード先フォルダの指定
dldir_path = Path('csv') # csv という名前のフォルダとする
dldir_path.mkdir(exist_ok=True) # なければ作成
download_dir = str(dldir_path.resolve()) # 絶対パスを取得
print("download_dir: " + download_dir)
options = webdriver.ChromeOptions()
options.add_experimental_option('prefs', { # Chrome のオプションに
'download.default_directory': download_dir # 絶対パスで指定
})
driver = webdriver.Chrome(options=options)
wait = WebDriverWait(driver, 10) # 明示的待機用 (Timeout 10秒)
# 自動操作開始
driver.get('https://www.data.jma.go.jp/gmd/risk/obsdl/index.php')
# 「地点を選ぶ」
xpath = '//div[@class="prefecture" and text()="東京"]'
time.sleep(2)
driver.find_element_by_xpath(xpath).click()
xpath = '//div[@class="station" and contains(@title, "地点名:東京")]'
time.sleep(2) # (★)
driver.find_element_by_xpath(xpath).click() # (★)
# 「項目を選ぶ」
driver.find_element_by_id('elementButton').click()
xpath = '//span[text()="月別値"]/preceding-sibling::input'
time.sleep(2)
driver.find_element_by_xpath(xpath).click()
css = '#日最高気温の平均'
time.sleep(2)
driver.find_element_by_css_selector(css).click()
# 「期間を選ぶ」
driver.find_element_by_id('periodButton').click()
time.sleep(2)
# <select>内の<option>要素を選択
Select(driver.find_element_by_name('iniy')).select_by_value('2010')
Select(driver.find_element_by_name('inim')).select_by_value('1')
time.sleep(2) # いったん止めてみる
Select(driver.find_element_by_name('endy')).select_by_value('2019')
Select(driver.find_element_by_name('endm')).select_by_value('12')
time.sleep(2)
# 「CSVファイルをダウンロード」
driver.find_element_by_id('csvdl').click()
time.sleep(2)
driver.quit()
図27 e-StatのAPI機能を利用した家計調査データを取得するプログラム
import sys
import urllib
import urllib.request
import json
import calendar
import matplotlib.pyplot as plt
import japanize_matplotlib # japanize-matplotlib を使う場合
import pandas as pd
from scipy import stats
url = 'https://api.e-stat.go.jp/rest/3.0/app/json/getStatsData?'
app_id = '<e-Statマイページで取得したアプリケーションIDを挿入>'
cat01 = '010800150' # アイスクリーム・シャーベット
# cat01 = '010800130' # チョコレート
# cat01 = '011100030' # ビール
remove_month = 0 # 特定月を除く場合は1-12のいずれかを指定
# 指定する数字の桁数は決まっているので注意
keys = {
'appId' : app_id,
'lang' : 'J',
'statsDataId' : '0003343671', # 家計調査データ
'metaGetFlg' : 'Y',
'cntGetFlg' : 'N',
'cdTab' : '01', # 金額
'cdTimeFrom' : '2010000101', # 2010年1月から
'cdTimeTo' : '2019001212', # 2019年12月まで
'cdArea' : '00000', # 全国
'cdCat01' : cat01,
'cdCat02' : '03' # 二人以上世帯
}
params = urllib.parse.urlencode(keys)
r_obj = urllib.request.urlopen(url + params) # データを取得
r_str = r_obj.read()
res = json.loads(r_str) # Pythonの辞書へ
stats_data = res['GET_STATS_DATA']['STATISTICAL_DATA']
class_obj = stats_data['CLASS_INF']['CLASS_OBJ'] # メタ情報
if 'DATA_INF' not in stats_data: # ['DATA_INF']が入らないときのチェック用
for co in class_obj:
if 'CLASS' not in co:
print("ERROR: Check params @id= {}, @name= {}" \
.format(co['@id'], co['@name']))
sys.exit(1)
values = stats_data['DATA_INF']['VALUE'] # 統計データの数値情報を取得
# メタ情報(CLASS_INF)から取得した品目名称を図のタイトルに使う
title = [co['CLASS']['@name'] for co in class_obj if co['@id'] == 'cat01'][0]
print(title)
# 各要素が [年, 月, 支出金額] の2次元リストにする
data = [[int(v['@time'][0:4]), int(v['@time'][6:8]), int(v['$'])] for v in values]
print("n =", len(data)) # 120 = 10年 x 12カ月
# Pandasデータフレームの準備
df = pd.DataFrame(data, columns=['year', 'month', '支出(円)'])
df['days'] = [calendar.monthrange(df.loc[i, 'year'], df.loc[i, 'month'])[1] for i in df.index] # 各月の日数
df['支出(円/日)'] = df['支出(円)'] / df['days'] # 1日あたりの支出金額
df['y/m'] = df['year'].astype(str) + '/' + df['month'].astype(str) # 結合用
# 気象庁の気温データとマージ
df_jma = pd.read_csv('csv/data.csv', skiprows=5, header=None, usecols=[0,1], encoding='Shift_JIS')
df_jma.columns = ['y/m', '平均最高気温(℃)']
df = pd.merge(df, df_jma, on='y/m') # データフレームの結合
if remove_month > 0:
df = df.query('month != @remove_month') # 特定月を除く場合
# 相関係数を計算
corr, _ = stats.pearsonr(df['平均最高気温(℃)'], df['支出(円/日)'])
corr_str = "相関係数: {:2f}".format(corr)
print(corr_str)
# 散布図をプロット
ax = df.plot(kind='scatter', x='平均最高気温(℃)', y='支出(円/日)')
ax.set_title(title + ', ' + corr_str)
plt.show()