水文水質データベースをスクレイピング

# やったこと
自己流の忘備録（書き途中）です。任意期間の河川の水質自動観測結果をPythonでスクレイピングし、csvとしてDL（手動だと７日間ごとしかできない）。エラー処理はまだ追加していない。それとcsvにヘッダーがない。改良する。

2019/9追記：あまり長い期間やるとはじかれる？ようです。２、３年くらいだとうまくいきましたが、５年だとだめでした。


# 流れ
http GETメソッドのパラメータ（取得したい地点、期間など）を設定し、リクエスト
↓
返ってきたhtmlをBeautifulSoupで解析し、必要な部分を抜き出す。
（html内のiframeという部分に、さらに別のURLでhtmlが参照されており、これが最終的な目的のデータとなる。）
↓
csvとして出力

# コード
```python
import csv
import time
from datetime import datetime
from datetime import timedelta
import requests
from bs4 import BeautifulSoup

#####↓関数を定義########################################################

#時間を、文字列←→日付型に相互に変換。どちらへ変換かは型で判別。
def time_tyconv(time):
    if isinstance(time,str):return datetime.strptime(time,'%Y%m%d')
    elif isinstance(time,datetime):return time.strftime("%Y%m%d")
    else:raise TypeError('type-err!')


#日付からステップ数を算出。
#一回にスクレイピングする期間の限度が７日間。次のステップは８日後。
def step_number(start_date, end_date):
    s = time_tyconv(start_date)
    e = time_tyconv(end_date)
    return int(((e-s).days + 1)//8)

#●日後の日付データを求める（start_dateの文字列をdatetime形式変換して７日足す→文字列に戻す）
def date_after_days(time,days):
    day_after = time_tyconv(time) + timedelta(days=days)#ここは日付型
    return time_tyconv(day_after)#ここで文字列になる。


#任意期間のデータを水水DBからスクレイピングする関数。
def scrp_DB(start_date, end_date, location_id,result_list):
    base_url = 'http://www1.river.go.jp'
    url = "http://www1.river.go.jp/cgi-bin/DspWquaData.exe"
    params = {
        "KIND":5,
        "ID":location_id,
        "BGNDATE":start_date,
        "ENDDATE":end_date,
        "KAWABOU":"NO"
    }
    #iframeのhtmlからsrcURLを取得
    resp = requests.get(url,params=params)
    temp_soup = BeautifulSoup(resp.text,'html.parser')
    resp_iframe = requests.get(base_url+temp_soup.iframe['src'])
    
    #srcURLのhtmlを取得
    #テーブル内のデータは、iframeにある。iframeのデータは別のhtmlに記述されている。
    #tableが２つあり、border = "●"で区別可能。border = "1"が目的の表。
    soup = BeautifulSoup(resp_iframe.text,'html.parser')
    soup = soup.select_one("table[border='1']")
    tr_list = soup.find_all("tr")
    tr_list.pop(0)
    #ヘッダー分をpop(0)で削除。
    
    for tr in tr_list:
        result_row = []
        td_list = tr.find_all('td')
        for td in td_list:
            cell = td.get_text()
            result_row.append(cell)
        result_list.append(result_row)
    
    return result_list
    
#######↓メインの流れ###################################################

#取得期間、地点番号（おそらく４から始まるほう）、保存ファイル名を設定
start_date = "20171201"
end_date = "20181031"
csvfile_name = "result_johoku.csv"
location_id = "403021283322090"
n_step = step_number(start_date, end_date)

result_list = []

#８の倍数回scrp_DBを実行
for n in range(n_step):
    result_list = scrp_DB(start_date, date_after_days(start_date,7), location_id,result_list)
    #start_dateに8日足して、次のループへ
    start_date = date_after_days(start_date,8)
    time.sleep(2)#サーバーへの負荷を考慮し、2秒置く

#８の倍数で余った期間でscrp_DBを実行
result = scrp_DB(start_date, end_date,location_id,result_list)

#CSVとして出力
with open(csvfile_name, 'w') as file:
    writer = csv.writer(file, lineterminator='\n')
    writer.writerows(result)
```

# つまづいたところ
## htmlのiframe(インラインフレーム)のデータ取得
htmlの中のiframeの中にさらにhtmlが参照されていて、こちらのURL（上記コードのsrcURL）を探してくる必要がある。WEBブラウザの開発ツールでiframeを覗くと、src属性？にURLが書いてあり、ベース?のURLにくっつけて目的のhtmlを取得する。
## tableのデータ取得
クジラ飛行机さんの本にちょうどtableのtr,tdを取得するやり方が載っていたので参考にしました。
## 日付
文字列型←→日付型が混乱した。今後自分用に追記もしくは別でまとめる。

#参考
+ クジラ飛行机さんの本（スクレイピングのやつ）
http://www.socym.co.jp/book/1079