2022/8/11 能登地方の群発地震ー今わかっていることー を投稿しました

日々の安心した生活は防災から!
「もしも」に備えるための防災グッズやアプリ、お役立ち情報などを発信中!

Pythonで火山カメラ画像をキャプチャする(2)

副産物

こんにちは、管理人のアカツキです。新たに「副産物」カテゴリを新設し、Pyhtonによる自作ツールの紹介をしています。その中から火山カメラ画像のキャプチャツールをピックアップし、前回からその解説を行っているところです。

解説は全体のコードを役割ごとにブロックとして分割し、各ブロックについて行うという形を取っています。第二回目はその続きをお送りします。

スポンサーリンク

(再掲)火山カメラ・キャプチャ コード

まずはプログラム全体のコードを再度お示しします。

"""
JMA火山監視カメラキャプチャ Ver.1.01
・JMAの火山監視カメラ画像をキャプチャする
・連続キャプチャを実施する(重複はスキップ)
・火山名をリストから選択する
・最初のリストはよくアクセスすると思われる火山
・火山リストをアップデート(20220727更新分)
"""
# ブロック1
from selenium import webdriver
from selenium.webdriver.chrome import service
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.common.action_chains import ActionChains
import os
import requests
import time

# ブロック2
dir_path = "(適当なディレクトリ)/volcano_camera/"

fav_volcano = {1:'十勝岳 白金模範牧場',
               2:'阿蘇山 草千里',
               3:'桜島 牛根',
               4:'諏訪之瀬島 寄木',
               5:'その他'}

area_volcano = {1:'北海道',
                2:'東北地方',
                3:'関東・中部地方',
                4:'伊豆・小笠原諸島',
                5:'九州地方',
                6:'戻る'}

hokkaido_volcano = {1: 'アトサヌプリ 北東山麓', 2: 'アトサヌプリ 硫黄山駐車場北',
                    3: 'アトサヌプリ 屈斜路湖南', 4: '雌阿寒岳 上徹別',
                    5: '雌阿寒岳 阿寒富士北', 6: '大雪山 忠別湖東',
                    7: '大雪山 旭岳姿見2', 8: '十勝岳 白金模範牧場',
                    9: '十勝岳 避難小屋南東', 10: '樽前山 別々川',
                    11: '倶多楽 414m山', 12: '倶多楽 地獄谷',
                    13: '有珠山 東湖畔', 14: '有珠山 月浦',
                    15: '北海道駒ヶ岳 鹿部公園南東', 16: '北海道駒ヶ岳 赤井川',
                    17: '北海道駒ヶ岳 剣ヶ峯', 18: '恵山 高岱',
                    19: '恵山 火口原', 20: '戻る'}

tohoku_volcano = {1: '岩木山 百沢東', 2: '八甲田山 大川原', 3: '八甲田山 地獄沼',
                  4: '十和田 銀山', 5: '秋田焼山 栂森', 6: '岩手山 柏台',
                  7: '岩手山 黒倉山', 8: '鳥海山 上郷2', 9: '栗駒山 大柳',
                  10: '栗駒山 展望岩頭', 11: '蔵王山 遠刈田温泉', 12: '蔵王山 上山金谷',
                  13: '蔵王山 刈田岳', 14: '蔵王山 御釜北', 15: '吾妻山 上野寺',
                  16: '安達太良山 若宮', 17: '安達太良山 鉄山', 18: '磐梯山 剣ケ峯',
                  19: '磐梯山 櫛ヶ峰', 20: '戻る'}

kanto_tyubu_volcano = {1: '那須岳 湯本ツムジケ平', 2: '那須岳 日の出平北', 3: '日光白根山 歌ヶ浜',
                       4: '日光白根山 上小川', 5: '草津白根山 奥山田', 6: '草津白根山 逢ノ峰山頂',
                       7: '草津白根山 草津', 8: '浅間山 鬼押', 9: '浅間山 追分',
                       10: '新潟焼山 宇棚', 11: '弥陀ヶ原 芦峅', 12: '焼岳 中尾峠',
                       13: '乗鞍岳 乗鞍高原', 14: '御嶽山 三岳黒沢', 15: '御嶽山 奥の院',
                       16: '白山 白峰', 17: '富士山 萩原', 18: '箱根山 宮城野',
                       19: '箱根山 大涌谷', 20: '箱根山 箱根峠', 21: '伊豆東部火山群 大原',
                       22: '伊豆東部火山群 大崎', 23: '戻る'}

izu_ogasawara_volcano = {1: '伊豆大島 北西外輪', 2: '伊豆大島 中央火孔北', 3: '新島 式根',
                         4: '神津島 前浜南東', 5: '三宅島 坪田', 6: '三宅島 神着',
                         7: '三宅島 山頂火口北西', 8: '八丈島 楊梅ヶ原', 9: '青ヶ島 手取山', 
                         10: '戻る'}

kyusyu_volcano = {1: '鶴見岳・伽藍岳 塚原無田', 2: '九重山 上野', 3: '九重山 星生山北尾根',
                  4: '九重山 飯田大原', 5: '阿蘇山 草千里', 6: '阿蘇山 車帰',
                  7: '阿蘇山 南阿蘇村', 8: '雲仙岳 野岳', 9: '雲仙岳 垂木台地南',
                  10: '霧島山 猪子石(新燃岳)', 11: '霧島山 猪子石(御鉢)', 12: '霧島山 御鉢火口南縁',
                  13: '霧島山 高原西麓', 14: '霧島山 八久保', 15: '霧島山 韓国岳',
                  16: '霧島山 えびの高原', 17: '霧島山 硫黄山南', 18: '桜島 牛根',
                  19: '桜島 東郡元', 20: '桜島 垂水荒崎', 21: '桜島 中央港新町',
                  22: '薩摩硫黄島 岩ノ上', 23: '口永良部島 本村西', 24: '口永良部島 屋久島吉田',
                  25: '諏訪之瀬島 寄木', 26: '諏訪之瀬島 キャンプ場', 27:'戻る'}

area_volcano2 = {'北海道':hokkaido_volcano,
                '東北地方':tohoku_volcano,
                '関東・中部地方':kanto_tyubu_volcano,
                '伊豆・小笠原諸島':izu_ogasawara_volcano,
                '九州地方':kyusyu_volcano}

# ブロック3
# メニューを行き来するためのフラグを設定
Flag = True
while Flag:
    for vkey1, vvalue1 in fav_volcano.items():
        print(f"{vkey1} : {vvalue1}")
    
    select_vkey1 = input("火山を選択してください [No.?] >>> ")
    select_vkey1 = int(select_vkey1)
    select_vvalue1 = fav_volcano[select_vkey1]
    
    # 1~6を選んだ場合の処理→値を取得してループ離脱
    if not select_vvalue1 == "その他":
        select_volcano = select_vvalue1
        break
    
    elif select_vvalue1 == "その他":
        
        # もう一つwhileを入れる
        while Flag:
            for vkey2, vvalue2 in area_volcano.items():
                print(f"{vkey2} : {vvalue2}")

            select_vkey2 = input("地域を選択してください [No.?] >>> ")
            select_vkey2 = int(select_vkey2)
            select_vvalue2 = area_volcano[select_vkey2]
            
            if select_vvalue2 == "戻る":
                # 再度前の画面に戻る
                break

            elif select_vvalue2 != "戻る":
                for vkey3,vvalue3 in area_volcano2[select_vvalue2].items():
                    print(f"{vkey3} : {vvalue3}")
             
                select_vkey3 = input("火山を選択してください [No.?] >>> ")
                select_vkey3 = int(select_vkey3)
                select_vvalue3 = area_volcano2[select_vvalue2][select_vkey3]
                
                if not select_vvalue3 == "戻る":
                    # 火山名を取得してループを抜ける
                    select_volcano = select_vvalue3
                    Flag = False

                elif select_vvalue3 == "戻る":
                    # 地域選択画面に戻る
                    pass

print(select_volcano,"を選びました")
print(select_volcano,"にアクセスします")

# ブロック4
chrome_driver = "(適当なディレクトリ)/chromedriver.exe"
chrome_service = service.Service(executable_path=chrome_driver)
driver = webdriver.Chrome(service=chrome_service)
actions = ActionChains(driver)
wait = WebDriverWait(driver=driver, timeout=10)

driver.implicitly_wait(5)
driver.set_window_position(50,50)
driver.set_window_size(1400,1300)

# JMAの監視カメラ画像のページに直接アクセスする
driver.get("https://www.data.jma.go.jp/vois/data/tokyo/volcam/volcam.php")
# wait.until(EC.presence_of_all_elements_located)

# 選択された火山カメラを開く
volcam = driver.find_element(By.LINK_TEXT,select_volcano)
volcam.click()

# wait.until(EC.presence_of_all_elements_located)

# スライダを左端に移動
driver.find_element(By.CSS_SELECTOR,"img[src='./icon/player/oldest.png']").click()

# 早送りボタンを取得
forward = driver.find_element(By.CSS_SELECTOR,"img[src='./icon/player/next-frame.png']")

# ブロック5
# 画像を保存する(繰り返し)
# 撮影枚数を取得
img_num = driver.find_element(By.XPATH,"//*[@id='main']/form/table[2]/tbody/tr[2]/td/table/tbody/tr[3]/td[5]/input[3]")
img_num = img_num.get_attribute("value")
time.sleep(1)

for i in range(int(img_num)):
    img_tag = driver.find_element(By.NAME,"myImg")
    img_url = img_tag.get_attribute("src")
    
    img = requests.get(img_url)
    file_name = os.path.basename(img_url)
    
    # 保存ディレクトリの作成
    # 取得した火山名を使う
    save_dir = os.path.join(dir_path,select_volcano)
    
    # if not文で判断→フォルダがない→False→if notはTrue→フォルダを作る
    if not os.path.isdir(save_dir):
        os.makedirs(save_dir)
    
    # 撮影の重複をチェック
    check_file = os.path.join(save_dir,file_name)
    
    # if notで判定する
    # 条件が真ならif notは偽を返す
    # 条件が偽ならif notは真を返す
    # ファイルがない=撮影していない=if notがTrue=Trueの処理=キャプチャする
    if not os.path.isfile(check_file):
        with open(check_file,"wb") as f:
            f.write(img.content)
    
    # 次の時間に移動
    forward.click()
    time.sleep(1)

driver.quit()
print("終わりました")

またプログラムの機能は次のようになっています。

JMA火山監視カメラキャプチャ Ver.1.01(volcano_capture.py)

  • 気象庁(JMA)が提供している火山監視カメラ画像について、
    任意の火山画像をキャプチャ(保存)する
  • キャプチャする火山名はリストから選択する(番号を入力して指定)
  • 画像を保存するフォルダ・ファイル名は自動で生成する
  • 火山リストを更新(2022.7.27 カメラ追加のプレスリリース)

続いてコードのブロック分割についてです。

ブロック一覧とその役割

  1. プログラムで使用するモジュールの呼び出し
  2. ファイル保存や火山名の選択に使う変数やデータ
  3. 火山選択メニュー
  4. 選択された火山カメラをブラウザで読み込む
  5. キャプチャした画像の保存処理

今回はブロック2について解説を進めていきます。

ブロック2・変数、データの設定

# ブロック2
dir_path = "(適当なディレクトリ)/volcano_camera/"

fav_volcano = {1:'十勝岳 白金模範牧場',
               2:'阿蘇山 草千里',
               3:'桜島 牛根',
               4:'諏訪之瀬島 寄木',
               5:'その他'}

area_volcano = {1:'北海道',
                2:'東北地方',
                3:'関東・中部地方',
                4:'伊豆・小笠原諸島',
                5:'九州地方',
                6:'戻る'}

hokkaido_volcano = {1: 'アトサヌプリ 北東山麓', 2: 'アトサヌプリ 硫黄山駐車場北',
                    3: 'アトサヌプリ 屈斜路湖南', 4: '雌阿寒岳 上徹別',
                    5: '雌阿寒岳 阿寒富士北', 6: '大雪山 忠別湖東',
                    7: '大雪山 旭岳姿見2', 8: '十勝岳 白金模範牧場',
                    9: '十勝岳 避難小屋南東', 10: '樽前山 別々川',
                    11: '倶多楽 414m山', 12: '倶多楽 地獄谷',
                    13: '有珠山 東湖畔', 14: '有珠山 月浦',
                    15: '北海道駒ヶ岳 鹿部公園南東', 16: '北海道駒ヶ岳 赤井川',
                    17: '北海道駒ヶ岳 剣ヶ峯', 18: '恵山 高岱',
                    19: '恵山 火口原', 20: '戻る'}

tohoku_volcano = {1: '岩木山 百沢東', 2: '八甲田山 大川原', 3: '八甲田山 地獄沼',
                  4: '十和田 銀山', 5: '秋田焼山 栂森', 6: '岩手山 柏台',
                  7: '岩手山 黒倉山', 8: '鳥海山 上郷2', 9: '栗駒山 大柳',
                  10: '栗駒山 展望岩頭', 11: '蔵王山 遠刈田温泉', 12: '蔵王山 上山金谷',
                  13: '蔵王山 刈田岳', 14: '蔵王山 御釜北', 15: '吾妻山 上野寺',
                  16: '安達太良山 若宮', 17: '安達太良山 鉄山', 18: '磐梯山 剣ケ峯',
                  19: '磐梯山 櫛ヶ峰', 20: '戻る'}

kanto_tyubu_volcano = {1: '那須岳 湯本ツムジケ平', 2: '那須岳 日の出平北', 3: '日光白根山 歌ヶ浜',
                       4: '日光白根山 上小川', 5: '草津白根山 奥山田', 6: '草津白根山 逢ノ峰山頂',
                       7: '草津白根山 草津', 8: '浅間山 鬼押', 9: '浅間山 追分',
                       10: '新潟焼山 宇棚', 11: '弥陀ヶ原 芦峅', 12: '焼岳 中尾峠',
                       13: '乗鞍岳 乗鞍高原', 14: '御嶽山 三岳黒沢', 15: '御嶽山 奥の院',
                       16: '白山 白峰', 17: '富士山 萩原', 18: '箱根山 宮城野',
                       19: '箱根山 大涌谷', 20: '箱根山 箱根峠', 21: '伊豆東部火山群 大原',
                       22: '伊豆東部火山群 大崎', 23: '戻る'}

izu_ogasawara_volcano = {1: '伊豆大島 北西外輪', 2: '伊豆大島 中央火孔北', 3: '新島 式根',
                         4: '神津島 前浜南東', 5: '三宅島 坪田', 6: '三宅島 神着',
                         7: '三宅島 山頂火口北西', 8: '八丈島 楊梅ヶ原', 9: '青ヶ島 手取山', 
                         10: '戻る'}

kyusyu_volcano = {1: '鶴見岳・伽藍岳 塚原無田', 2: '九重山 上野', 3: '九重山 星生山北尾根',
                  4: '九重山 飯田大原', 5: '阿蘇山 草千里', 6: '阿蘇山 車帰',
                  7: '阿蘇山 南阿蘇村', 8: '雲仙岳 野岳', 9: '雲仙岳 垂木台地南',
                  10: '霧島山 猪子石(新燃岳)', 11: '霧島山 猪子石(御鉢)', 12: '霧島山 御鉢火口南縁',
                  13: '霧島山 高原西麓', 14: '霧島山 八久保', 15: '霧島山 韓国岳',
                  16: '霧島山 えびの高原', 17: '霧島山 硫黄山南', 18: '桜島 牛根',
                  19: '桜島 東郡元', 20: '桜島 垂水荒崎', 21: '桜島 中央港新町',
                  22: '薩摩硫黄島 岩ノ上', 23: '口永良部島 本村西', 24: '口永良部島 屋久島吉田',
                  25: '諏訪之瀬島 寄木', 26: '諏訪之瀬島 キャンプ場', 27:'戻る'}

area_volcano2 = {'北海道':hokkaido_volcano,
                '東北地方':tohoku_volcano,
                '関東・中部地方':kanto_tyubu_volcano,
                '伊豆・小笠原諸島':izu_ogasawara_volcano,
                '九州地方':kyusyu_volcano}

画像の保存場所を決める

dir_path = "(適当なディレクトリ)/volcano_camera/"

まず最初の行を見てみましょう。ここではキャプチャした画像を保存するためのディレクトリを用意しdir_pathに格納しています。volcano_cameraというフォルダの直下に火山別の画像フォルダを作成し、画像を保存するようにしています。

ここの内容はあなたの環境に合わせてカスタマイズをお願いします。

火山の辞書データを設定

続いて4行目以降のごちゃごちゃした部分です。見ると地域や火山名が入ったデータになっていますね。次のブロック3において、火山名を選択する処理を行いますが、ここのデータはそれらの処理に使われます。そのための準備をしている、ということになります。

また、データ型は全て辞書型になっています。Pythonには複数のデータを扱う型にリスト辞書があります。特に後者はキーという組み合わせでデータが入っており、あるキーに対応した値を取り出すことができます。

冒頭にもお示しした通り、本プログラムは火山名の入力を番号で選択する設計になっています。番号を入力すると火山名が呼び出される…これってキーと値という関係になっていますよね?それでPythonならではのデータ型である辞書を使うのがぴったりだと思いまして採用してみました。

辞書データは上からfav_volcanoarea_volcano…とありますが、それぞれの意味は次のようになっています。

  • fav_volcano
    よくアクセスする火山をまとめたもの
  • area_volcano
    任意の火山を選ぶときに地域の絞り込みを行うための地域一覧
  • hokkaido_volcano, tohoku_volcano, …, kyusyu_volcano
    各地域でカメラ提供されている火山一覧
    (hokkaido_volcanoなら北海道の火山といった具合です)
  • area_volcano2
    ブロック3の内部処理用
    キーを番号ではなく地域名にして値を各地域の火山一覧にしたもの
    (辞書に辞書をネストしている)

端的に言うと、火山カメラの全リストということになります。ですので最初にこのリストを自分で作成する必要があります。

一方で、下にお示ししたように火山カメラの数は100近くあり、まずこれをリスト化するのが一つのハードルになりました。

火山カメラ一覧
火山カメラの一覧(全部で94あります)
2022.7.27に6つのカメラが追加されています
出展 気象庁「監視カメラ画像

おそらくはこの部分だけをテキストファイルとして外部から読み込んだり、あるいはサイトにアクセスしたときに動的に読み込むなどすれば良いのでしょうが、このプログラムを作成した時点でそこまでできるようなコードが浮かびませんでした。

とはいってももう作業したくないと思えるような途方もない数でもないので、これくらいなら良いかな…と思って辞書に突っ込んだという顛末になります。

たださすがにデータをぽちぽち手入力するのはしんどいですし目が悲鳴を上げそうです。無理をしたところでhokkaido_volcanoの三つ目あたりで早くもコロンを失念したりコンマを余計に打ちそうなので、目薬を差す代わりに一部Pythonに助けてもらい、そこから生成されたものを使って火山辞書を作成しました。そのために書いたサブプログラムのコードはこちらです。

なお、この方法で辞書を作成した後に火山カメラの追加が行われました。その追加分については手入力で反映させています。以降のサブプログラムに関する記述においては、カメラ追加前のデータになっていることにご注意ください。

# 辞書型作成
# 手入力でリストを作るのが面倒
# 監視カメラのエリア一覧からdict関数で辞書型を作ってみる
# データ作成後に火山カメラ追加が行われたので、追加分は手入力で反映

hokkaido_number = list(range(1,20))
hokkaido_volcano = ["アトサヌプリ 北東山麓","アトサヌプリ 硫黄山駐車場北",
                    "アトサヌプリ 屈斜路湖南","雌阿寒岳 上徹別",
                    "雌阿寒岳 阿寒富士北","大雪山 忠別湖東",
                    "大雪山 旭岳姿見2","十勝岳 白金模範牧場",
                    "十勝岳 避難小屋南東","樽前山 別々川",
                    "倶多楽 414m山","倶多楽 地獄谷",
                    "有珠山 東湖畔","有珠山 月浦",
                    "北海道駒ヶ岳 鹿部公園南東","北海道駒ヶ岳 赤井川",
                    "北海道駒ヶ岳 剣ヶ峯","恵山 高岱","恵山 火口原"]

tohoku_number = list(range(1,21))
tohoku_volcano = ["岩木山 百沢東","八甲田山 大川原","八甲田山 地獄沼",
                  "十和田 銀山","秋田焼山 栂森","岩手山 柏台",
                  "岩手山 黒倉山","鳥海山 上郷2","栗駒山 大柳",
                  "栗駒山 展望岩頭","蔵王山 遠刈田温泉","蔵王山 上山金谷",
                  "蔵王山 刈田岳","蔵王山 御釜北","吾妻山 上野寺",
                  "安達太良山 若宮","安達太良山 鉄山","磐梯山 剣ケ峯",
                  "磐梯山 櫛ヶ峰"]

kanto_tyubu_number = list(range(1,23))
kanto_tyubu_volcano = ["那須岳 湯本ツムジケ平","那須岳 日の出平北","日光白根山 歌ヶ浜",
                       "日光白根山 上小川","草津白根山 奥山田","草津白根山 逢ノ峰山頂",
                       "草津白根山 草津","浅間山 鬼押","浅間山 追分",
                       "新潟焼山 宇棚","弥陀ヶ原 芦峅","焼岳 中尾峠",
                       "乗鞍岳 乗鞍高原","御嶽山 三岳黒沢","御嶽山 奥の院",
                       "白山 白峰","富士山 萩原","箱根山 宮城野",
                       "箱根山 大涌谷","箱根山 箱根峠","伊豆東部火山群 大原","伊豆東部火山群 大崎"]

izu_ogasawara_number = list(range(1,9))
izu_ogasawara_volcano = ["伊豆大島 北西外輪","伊豆大島 中央火孔北","新島 式根",
                         "神津島 前浜南東","三宅島 坪田","三宅島 神着",
                         "八丈島 楊梅ヶ原","青ヶ島 手取山"]

kyusyu_number = list(range(1,21))
kyusyu_volcano = ["鶴見岳・伽藍岳 塚原無田","九重山 上野","九重山 星生山北尾根",
                  "九重山 飯田大原","阿蘇山 草千里","阿蘇山 車帰",
                  "雲仙岳 野岳","雲仙岳 垂木台地南","霧島山 猪子石(新燃岳)",
                  "霧島山 猪子石(御鉢)","霧島山 八久保","霧島山 韓国岳",
                  "霧島山 えびの高原","霧島山 硫黄山南","桜島 牛根",
                  "桜島 東郡元","薩摩硫黄島 岩ノ上","口永良部島 本村西",
                  "口永良部島 屋久島吉田","諏訪之瀬島 寄木","諏訪之瀬島 キャンプ場"]

hkd_volcano = dict(zip(hokkaido_number,hokkaido_volcano))
thk_volcano = dict(zip(tohoku_number,tohoku_volcano))
knt_volcano = dict(zip(kanto_tyubu_number,kanto_tyubu_volcano))
izu_volcano = dict(zip(izu_ogasawara_number,izu_ogasawara_volcano))
kyu_volcano = dict(zip(kyusyu_number,kyusyu_volcano))

print(hkd_volcano)
print(thk_volcano)
print(knt_volcano)
print(izu_volcano)
print(kyu_volcano)

ではその中身は何をやっていらっしゃるのかしら?ということですが

  1. キー(番号)をlist関数とrange関数で作成
  2. 対応する値(火山名)をリスト型で用意
  3. 辞書型を作成するdict関数で辞書型を作成する

を行っています。つまり、リスト型データを辞書型データに加工しています。

また地域は北海道、東北、関東・中部、伊豆・小笠原、九州の五つに分けられていますが、そのいずれについても同じ操作を行っています。ここでは例として北海道について見ていきます。

サブプログラム(1) キー番号リストの作成

まずキーの作成についてです。実行結果は次のようになり、それをhokkaido_numberに代入しています。

hokkaido_number = list(range(1,20))
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

キーは番号でしたから、それを生成するためにrange()を使用しています。連続する数値やたとえば3ずつ増えていくような数(等差数列ですね)を作ってくれる組み込み関数です。

それをlist()関数でリスト型のデータに変換しています(range関数で作ったデータはrange型なので型が異なるため)。

単に連続する数値を作りたい時には

range(終了値)

# 0~2まで連続した数値を得るには?
x = range(3)
print(list(x))
# [0,1,2]

のように終了する値を入力してあげます。実際には0から生成されるため、終了値-1までの数値になることに注意が必要です。

今回の場合ですとキー番号は1から始まっています。このように開始値を指定したいときには

range(開始値,終了値[,ステップ])

# 1からはじまり、3まで連続した数値を作る
x = range(1,4)
print(list(x))
# [1,2,3]

と終了値の前に指定します。またステップ値を入れれば3ずつ飛ばした等差数列的な数値も作ることができます。

サンプルに戻りますと、北海道の火山数は19あり、辞書データはキーと値が基本的に1対1で対応しますから、必要なキー番号は1~19となります。そのためlist(range(1,20))としてキー番号のリストを作成しています。

サブプログラム(2) 火山名のリスト作成

続いて火山名のリストです。こちらもあまり手入力はしたくないですよね…。そこで先ほどもお示ししましたが、火山監視カメラ画像のページをもう一度ご覧ください。

火山カメラ一覧
火山カメラの一覧(再掲)
出展 気象庁「監視カメラ画像

要はここに全火山リストがあるのですから、これをそのまま選択してコピー後、適当なテキストエディタに貼り付けてリスト型に整形すれば火山リストを作ることができます。

作成したリストは、まとめてhokkaido_volcanoに代入します。クォーテーションとコンマを入力していく手間がありますが、本来かけるべき作業量は大幅に減っていますので目薬は必要ありません。

サブプログラム(3) 辞書データの作成

ここまでの作業でめでたくキーと値が出そろいました。これを辞書データに加工すれば作業は終わりです。さぞ複雑な作業を要するのではと当初思っていましたが、実際に書いてみるとわずか一行のコードで終了しました。

hkd_volcano = dict(zip(hokkaido_number,hokkaido_volcano))

右辺を見ますと、二つの関数dict()zip()が使われています。まずはdict()関数について見ていきます。

dict()関数はその名前(dictionary)の通り、辞書型を作る関数です。使い方は色々ありますが、ここではキーという二つの要素を持つシーケンス(複数の要素が順番に入ったデータ型。リストやタプル、range型)から作ることを想定しています。

# dict()関数の使い方

# キーと値の二要素からなるリストを作成
phonetic = [[1,"alpha"],[2,"bravo"],[3,"charlie"]]

# dict()の引数にphoneticを入れて辞書を作成し、dに代入
d = dict(phonetic)
print(d)
# {1: 'alpha', 2: 'bravo', 3: 'charlie'}

phoneticの中身はリスト内リストになっており、辞書にしたいキーと値のペアがそれぞれリストの一要素として入っています。ですのでシーケンスはいわゆるニコイチの関係にしないとエラーが発生します。

なるほど、ではキーリストであるhokkaido_numberと値リストのhokkaido_volcanoがあるのだから

hkd_volcano = dict(hokkaido_number,hokkaido_volcano)

で辞書になるよね!…と思ってしまうかも知れませんがこれはエラーが出て怒られます。

# TypeError: dict expected at most 1 argument, got 2
# TypeError: dict は最大1つの引数を予期していたが、2つの引数を得た。

前例のように引数に入れるシーケンスは一つじゃないとダメ、ということのようです。そのシーケンスはキーと値のペアが一つの要素になっていないといけません。

この例では、キーのリストと値のリストはそれぞれ別々になっていますから、キーと値でまとめないと受け付けてくれないのですね。

ではキーと値でまとめればOKなんですね?ということで使うのがzip()関数です。

# zip()関数でキーと値のペアを作る

a = [1,2,3]
b = ["alpha","bravo","charlie"]

# zip(シーケンス1,シーケンス2)
# シーケンスは2個以上でもOK
z = zip(a,b)

# データを取り出してみる
for key,value in z:
    print("キーは",key,"値は",value)

# キーは 1 値は alpha
# キーは 2 値は bravo
# キーは 3 値は charlie

この関数でzip(シーケンス1、シーケンス2)としてやることで、それぞれのシーケンスから要素を順番に一つずつ取り出してくれます。一つずつ取り出された要素はペアの関係になります。

後段のfor key,value in z:で本当に要素が取り出されたのかを確認しています。キーと値が対応してニコイチのペアになっていることが分かりますね。

後はzip()dict()を組み合わせてやれば目的は達成です。実際にブロック2のサンプルコードにあるhokkaido_volcanoが得られているか確認してみてください。ただこの後の処理も考えて戻るなどの値を追加したりコードの見た目上改行を加えていることに注意してください。

いずれにしてもサブプログラムの使用で火山辞書の作成がかなり楽になりました。

まとめです

「Pythonによる火山カメラのキャプチャ」第二回目をお送りしました。今回の部分までがプログラムの準備段階と言えるところで、次回から本格的な処理部分が始まります。みんなが大好き?ループ処理が登場します。

本プログラムは、webスクレイピングというwebブラウザを自動的に操作してサーバに接続し、データを取得する技術を使用しています。

サーバへのアクセスは過度に負荷を掛けず、人が普通に操作しているように調整しています。プログラムの使用・改変は自由ですが、もしご利用をされる場合があれば、適切な利用をお願いします。

タイトルとURLをコピーしました