【Python】requestsモジュールでJSONDecodeErrorが出た

Moi!
こんにちは、ロニーです。

今回はPythonのrequestsモジュールに関して躓いた話です。

 

requestsという、
インターネット上からデータを取得していい感じにスクレイピングしてくれるモジュールがあります。

「世界でいちばん簡単なPythonプログラミングのe本(第2版)」
(金城俊哉著、秀和システム)

の中の課題で、
Weather Hacksが提供しているAPIから天気情報を取得してくる部分で躓いたのでメモします。

Pythonプログラミングのe本 第2版 表紙

JSONDecodeErrorが現れた!

テストコードをJupyter Notebookに書き写して実行してみたところ、
エラーが帰ってきました。

なるほど、JSONDecodeErrorですね。(よくわからん。)

JSONDecodeError

これを見ると、
取得してきたデータをJSON形式からデコードするときにエラーが生じてます。

ということは、
取得してきたデータがJSON形式になってない可能性が考えられます。

JSON形式じゃないから、デコードが出来ていないと仮定しましょう。

なぜJSON形式じゃなくなっているのか。

指定のURLを試しに(クエリ抜きで)検索してみると、
Livedoorブログのトップページに遷移しました。

 

調べてみたら、
どうやらWeather Hacksのサービスは2020年7月に終了していたそう。

なるほどですね。
それでJSON形式ではないレスポンスが帰ってきて、
.json()メソッドの実行にエラーが出たんだな、と考えられます。

代わりに、同様にJSON形式で提供しているサービスがあるのでは、
と調べたら、
同じようにLivedoorWeatherHacksから気象庁のデータに切り替えたサービスがあるのを発見しました。

Weather.tsukumijima.net

※追記:2022年7月29日現在、
上記のURLも404を返すようになってました。
他に似たサービスがないか探してみてください。

JSON形式でデータを提供しているURLに差し替えて解決

ではリクエストを送るURLを修正してみます。
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

import requests
url = 'https://weather.tsukumijima.net/api/forecast/city/130010'
data = requests.get(url).json()
data

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
urlを差し替え後

帰ってきました!
最下部を見ると、

最下部のメモ

と書かれています。

気象庁が提供しているデータを、
tsukumojima.net さんがJSON形式へと変換して配信しているんですね。

 

記載のURLで気象庁の
気象データ高度利用ポータルサイト
を訪れてみると、

CSV形式などでデータを配信しているのがわかりますが、
確かにJSONで取得するための入り口は見付かりません。

どうやって変換しているのだろう。

スクレイピングで欲しい情報だけ抜き出す

さて、このままのレスポンス量だと冗長的なので絞り込んでみます。

JSONファイルはPythonでの辞書型と完全に一致するそうなので、
キーを指定すればバリューを取得できます。

キーはブラケットで囲ったインデックスを使って指定できます。

辞書型(dictionary型)はこういうやつです。
一応おさらいまでに。

{‘name’: ‘ロニー’}

ブレースで囲った中に要素を書き込みます。

コロン挟んで左がキー、右がバリューで、
キーを引数に渡すとバリューを返してくれるっていうデータ型ですね。

forecastsキーに対応するバリューはリストになっていて、
[0]は今日、[1]が明日、[2]が明後日だということがわかります。

リスト型は要素をその位置、
すなわちインデックスを渡すことで要素を取得できます。
こういうやつです。

masters = [‘ガンダルフ’, ‘オジオン’, ‘ダンブルドア’]
print(masters[0])
>>>ガンダルフ

辞書の中にリストがあって、
その中に辞書があるという入れ子になっているので分かりづらいですが、
簡易的に書くとこう。

{'forecast':[
    {今日に関するデータ},
    {明日に関するデータ},
    {明後日に関するデータ}
    ]
}

ちなみに試しに[3]を指定したらエラーが帰ってきました。

存在しないインデックスは指定できません。
今日の分のを指定してみましょう。

今日の日付のインデックスを指定

さらに絞り込んで、telopのバリューで予報されている天気を指定してみます。

telopで絞り込み

抽出できました!

 

最後に、テキスト通りに、
forループでforecasts内からリスト要素を順にweather変数に格納し、
dateLabelキーとそれに対応するtelopをつなげて出力する例を実行してみます。

ちなみにこの時点でURLはcity/130010まで指定していますが、
これはクエリ文字列にしても有効です。

クエリ文字列で指定

この後の例題では都市と地域コードを指定するのを、
URLやクエリ文字列ではない形で指定していました。

キーワード引数です。

キーワード引数を指定

requestsでは、
paramsキーワード引数を使ってリクエストメソッドにキー・バリューペアを渡すことが出来ます。

一旦ペアをpayload変数に代入し、
それを引数として渡しているわけですね。

実行結果はきちんと出力されました。

エラーは出ないですが、
URLにも都市と地域コードを含めてしまっているのであまり参考にならないですね…。

提供しているURLやファイル形式、キーバリュー設定などで対応が変わってくるので、
その都度提供元APIの仕様を確認しないといけなそうです。

おわりに

普段天気を知りたいときは、在宅中はGoogleHomeに尋ねるだけだし、
外出中はiPhoneのアプリをチェックする形で対応しているので、
わざわざスクレイピングで何かしらのアプリに天気予報機能を盛り込む必要はないんですけどね。

requestsでJSON形式のデータをスクレイピングするための例として学習素材にしました。
じゃ次の課題に移ろう。

今回は以上!
モイモイ!

コメント

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