読者です 読者をやめる 読者になる 読者になる

歩いたら休め

If the implementation is easy to explain, it may be a good idea.

【Python】Yahoo!天気をクロールして各県の過去のお天気データを取りたかった

Python

過去の各県のお天気データが欲しかったのでYahoo!天気をクロールしてみようとしています。 まずは地域×年月日のURL一覧を列挙してみました。

気象庁のデータが理想だったのですが、いちいちダウンロードしないといけないやつで困っていたのですが、Yahoo!天気を教えていただきました。 www.data.jma.go.jp

一箇所、リスト内包表記で詰まっしまいました。

当初queries = list(map(make_query, dates))をリストに変換せずにジェネレータのまま使っていたのですが、params = [[p, q] for p in pref_codes for q in queries]の際、最初のpref_codesqueriesの値が消費されて消えてしまい、全ての組み合わせを列挙できませんでした。

# 年月日を作成
years = range(2010,2016)
months = range(1,13)
days = range(1,32)
dates = [[c,m,d] for c in years for m in months for d in days]

# 存在しない組み合わせを削除
dates = filter(lambda x: not ((x[1] in [2,4,6,9,11]) and x[2] == 31), dates)
dates = filter(lambda x: not (x[1] == 2 and x[2] == 30), dates)
dates = filter(lambda x: not (x[1] == 2 and x[2] == 29 and not x[0] == 2012), dates)

# 欲しい期間外の日付を削除
dates = filter(lambda x: not (x[0] == 2010 and x[1] <= 9), dates)
dates = filter(lambda x: not (x[0] == 2010 and x[1] == 10 and x[2] <= 11), dates)
dates = filter(lambda x: not (x[0] == 2015 and x[1] >= 9), dates)

# クエリを作成
def make_query(date):
    date = [str(x) for x in date]
    return '?c=' + date[0] + '&m=' + date[1] + '&d=' + date[2]
queries = list(map(make_query, dates))

# 都道府県の県庁所在地のURLを作成
# 埼玉と岐阜とか、場所が微妙なところがあるかも
pref_codes= ['1b/1400', '2/3110', '3/3310', '4/3410', '5/3210',
             '6/3510', '7/3610', '8/4010', '9/4110', '10/4210',
             '11/4330', '12/4510', '13/4410', '14/4610', '15/5410',
             '16/5510', '17/5610', '18/5710', '19/4910', '20/4810', 
             '21/5220', '22/5040', '23/5110', '24/5310', '25/6020', 
             '26/6110', '27/6200', '28/6310', '29/6410', '30/6510',
             '31/6910', '32/6810', '33/6610', '34/6710', '35/8120',
             '36/7110', '37/7200', '38/7310', '39/7410', '40/8210',
             '41/8510', '42/8410', '43/8610', '44/8310', '45/8710',
             '46/8810', '47/9110']

# urlのパラメータリストを作成
params = [[p, q] for p in pref_codes for q in queries]
params = map(lambda x: (x[0] + '/detail.html' + x[1]), params)

# urlの組み合わせを列挙
base_url = 'http://weather.yahoo.co.jp/weather/jp/past/'
urls = map(lambda x: base_url + str(x), params)

# 一行ずつ書き込む
with open('output.txt', 'w') as f:
    for url in urls:
        f.write(url)
        f.write('\n')

こんな結果が出ます。

http://weather.yahoo.co.jp/weather/jp/past/1b/1400/detail.html?c=2010&m=10&d=12
http://weather.yahoo.co.jp/weather/jp/past/1b/1400/detail.html?c=2010&m=10&d=13
http://weather.yahoo.co.jp/weather/jp/past/1b/1400/detail.html?c=2010&m=10&d=14
http://weather.yahoo.co.jp/weather/jp/past/1b/1400/detail.html?c=2010&m=10&d=15
http://weather.yahoo.co.jp/weather/jp/past/1b/1400/detail.html?c=2010&m=10&d=16
http://weather.yahoo.co.jp/weather/jp/past/1b/1400/detail.html?c=2010&m=10&d=17
http://weather.yahoo.co.jp/weather/jp/past/1b/1400/detail.html?c=2010&m=10&d=18

数万行あったので、真面目にクロールするとサイバー攻撃になってしまうんじゃないかと危惧しています。ある程度間隔を開けてアクセスするとか、ちょっとやり方を考えてみます。

(といっても、1秒に1アクセスでも約1日かかっちゃうんだよなあ…)