Python&スクレイピングで気象情報を取得する

気象庁が公開している気象データをスクレイピングして取得し、csvファイルとして出力するPGを健忘録としてまとめておきます。

#参考
[Pythonで天気APIを使って天気の情報を取得する方法](https://python.blueskyzz.com/python/post-138/)
[Webスクレイピングの注意事項一覧](https://qiita.com/nezuq/items/c5e827e1827e7cb29011)
[Python Webスクレイピング テクニック集「取得できない値は無い」JavaScript対応@追記あり6/12]
(https://qiita.com/Azunyan1111/items/b161b998790b1db2ff7a)

#環境
OS : Windows10 64 bit
python : 3.7.4

| 主なパッケージ名       |       用途 | 
|:-----------------|:------------------:|
| BeautifulSoup|              Webページの解析＆タグの検索&データの整形 | 
| logging|            ログの出力や設定|          
| namedtuple|                タプル操作|      
| pandas |             Dataframe操作用|     
|requests |webページの取得
#　手順
やり方はいたって簡単。
①　requestsというパッケージを使用して、取得したいWebページ内のデータを取得する

```Python:
r = requests.get('URL')
```
②　BeautifulSoupというパッケージで中身を解析

```Python:
soup = BeautifulSoup(r.text,'lxml')
                rows = soup.findAll("tr",class_="mtx") #条件を絞ってタグを取得
```

③　pandasでDataframeとしてまとめ、csvとして出力

```Python:
       weatherData = pd.DataFrame(dataList[1:])
       # 列名の設定
       weatherData.columns  = nameList
       # csvファイルとして出力
       weatherData.to_csv(f"{place}{startYear}_{endYear}.csv",encoding="SHIFT-JIS")
```

# 作成例
```Python:GetWeather.py
# coding: UTF-8
#*************************************************************************
# 天気情報取得処理
#
# 対象データ:福岡県内の天気情報
# 処理内容：気象庁から気象情報を取得し、csvファイルを出力する
#           ※気象庁利用規約より、商用利用可であることを確認
#           https://www.jma.go.jp/jma/kishou/info/coment.html
# python ver = 3.7.4
#*************************************************************************
import os
import sys
import requests 
import logging.config
from time import time
import datetime as dt
import sqlite3
import pprint
import pandas as pd
from bs4 import BeautifulSoup 
from collections import namedtuple
import csv
#*************************************************************************
# 変数一覧
#*************************************************************************
now = dt.datetime.now() # 現在日時
now =now.strftime("%Y%m%d")

# exeが存在するファイル位置
# CODEが存在する絶対パスの2つ前を作業フォルダに設定
# 必要に応じて自分の作業ファイルとしたい場所を指定する用に変更しよう
os.chdir(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../"))
exePath = os.getcwd()

place_codeA = [82]
place_codeB = [47807]
place_name = ["福岡"] 
nameList = ["地点","年月日", "陸の平均気圧(hPa)", "海の平均気圧(hPa)","'降水量(mm)", "平均気温(℃)", "平均湿度(%)", "平均風速(m/s)", "日照時間(h)","昼(06:00-18:00)","夜(18:00-翌日06:00)"]
dataList = [nameList]
collist = ["Point"]
base_url = "http://www.data.jma.go.jp/obd/stats/etrn/view/daily_s1.php?prec_no=%s&block_no=%s&year=%s&month=%s&day=1&view=p1"

#***********************************************
# 名前付きタプルリスト一覧
#*************************************************************************
#*************************************************************************
#[ ONL : OutputNameList] 出力/集計処理用列名のタプル
#*************************************************************************
# 列名0-9,10-19,20-29
partsO =["PT","TIME", "EHPA","SHPA", "RAIN", "TEMPER", "HUMID","WIND","SUM","AM","PM"]

OLNM = namedtuple("ONL", partsO)

OCNL = OLNM("地点","年月日", "陸の平均気圧(hPa)", "海の平均気圧(hPa)","'降水量(mm)", "平均気温(℃)", "平均湿度(%)", "平均風速(m/s)", "日照時間(h)","昼(06:00-18:00)","夜(18:00-翌日06:00)")

#*************************************************************************
# 関数一覧
#*************************************************************************
#*************************************************************************
# ParseFloat:文字列をfloat型に変換する
#       引数:
#*************************************************************************
def ParseFloat(str):
  try:
    return float(str)
  except:
    return 0.0
#*************************************************************************
# メイン処理
#       引数:
#*************************************************************************
def main():
   returnValue = 0
   
   # ログ設定
   logging.config.fileConfig(f"{exePath}/python/logging.conf", defaults={'logfilename': f"log/UI_{now}.log" })
   logger = logging.getLogger()
   logger.info("気象情報取得処理 記録開始")
   
   startYear = 2018
   endYear = 2018
   try:
       # 場所でループ
       for place in place_name:
          index = place_name.index(place)
          
          # for文で2018年~2019年まで
          for year in range(startYear,endYear + 1):
          
             # その年の1月~12月の12回を網羅する。
             for month in range(1,13):
                #2つの都市コードと年と月を当てはめる。
                r = requests.get(base_url%(place_codeA[index], place_codeB[index], year, month))
                r.encoding = r.apparent_encoding
                print(dt.datetime.now())
                # 対象である表をスクレイピング。
                soup = BeautifulSoup(r.text,'lxml')
                rows = soup.findAll("tr",class_="mtx") #タグ指定してclass名を指定する
                
                rows = rows[4:]
                # 1日〜最終日までの１行を網羅し、取得
                for row in rows:
                   data = row.findAll("td")
                   # 欲しい情報のみを抽出
                   rowData = [] # 初期化
                   rowData.append(place) # 地点
                   rowData.append(str(year) + "/" + str(month) + "/" + str(data[0].string)) # 年月日
                   rowData.append(ParseFloat(data[1].string)) # 陸の平均気圧(hPa)
                   rowData.append(ParseFloat(data[2].string)) # 海の平均気圧(hPa)
                   rowData.append(ParseFloat(data[3].string)) # '降水量(mm)
                   rowData.append(ParseFloat(data[6].string)) # 平均気温(℃)
                   rowData.append(ParseFloat(data[9].string)) # 平均湿度(%)
                   rowData.append(ParseFloat(data[11].string)) # 平均風速(m/s)
                   rowData.append(ParseFloat(data[16].string)) # 日照時間(h)
                   rowData.append(data[19].string) # 昼(06:00-18:00)
                   rowData.append(data[20].string) # 夜(18:00-翌日06:00)
                   #次の行にデータを追加
                   dataList.append(rowData)
       
       # Dataframe化する
       weatherData = pd.DataFrame(dataList[1:])
       # 列名の設定
       weatherData.columns  = nameList
       print(weatherData)
       # csvファイルとして出力
       weatherData.to_csv(f"{place}{startYear}_{endYear}.csv",encoding="SHIFT-JIS")
   # 例外が発生した場合
   except:
       logger.info("気象情報取得処理 異常終了")
       logger.exception("【異常終了しました】")
   # 正常終了した場合
   else:
       logger.info("気象情報取得処理 正常終了")
   finally:
       print("{0}".format(returnValue), end="")
                
# パッケージとしてimportしたのではなければ、以下を実行
if __name__ == "__main__":
   main()
```