LoginSignup
7
8

More than 3 years have passed since last update.

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

Last updated at Posted at 2018-11-21

やったこと

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

2019/9追記:あまり長い期間やるとはじかれる?ようです。2、3年くらいだとうまくいきましたが、5年だとだめでした。

流れ

http GETメソッドのパラメータ(取得したい地点、期間など)を設定し、リクエスト

返ってきたhtmlをBeautifulSoupで解析し、必要な部分を抜き出す。
(html内のiframeという部分に、さらに別のURLでhtmlが参照されており、これが最終的な目的のデータとなる。)

csvとして出力

コード

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!')


#日付からステップ数を算出。
#一回にスクレイピングする期間の限度が7日間。次のステップは8日後。
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形式変換して7日足す→文字列に戻す)
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が2つあり、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

#######↓メインの流れ###################################################

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

result_list = []

#8の倍数回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秒置く

#8の倍数で余った期間で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を取得するやり方が載っていたので参考にしました。

日付

文字列型←→日付型が混乱した。今後自分用に追記もしくは別でまとめる。

参考

7
8
8

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
8