はじめに
言語処理100本ノック 2015という素敵教材をド素人が挑戦していく記事です。
今まで数回分まとめて書いていたのですが、今回は第3章1本目ということもあり、
JSON関連で色々躓いたのでその備忘録を書くためにNo.20だけにしました。
ちなみにこれまでのノック
〜言語処理100本ノック No.00-04〜
〜言語処理100本ノック No.05-09〜
〜言語処理100本ノック No.10-14〜
〜言語処理100本ノック No.15-19〜
環境
Mac OS X 10.14.5
Python 3.7.1
準備
Wikipediaの記事を以下のフォーマットで書き出したファイルを使います。
(詳しくはサイト参照)
- 1行に1記事の情報がJSON形式で格納される
- 各行には記事名が"title"キーに,記事本文が"text"キーの辞書オブジェクトに格納され,そのオブジェクトがJSON形式で書き出される
- ファイル全体はgzipで圧縮される
20. JSONデータの読み込み
Wikipedia記事のJSONファイルを読み込み,「イギリス」に関する記事本文を表示せよ.問題21-29では,ここで抽出した記事本文に対して実行せよ.
実行コマンド
import json
import gzip
with gzip.open('wiki_data/jawiki-country.json.gz') as f:
for line in f:
data = json.loads(line)
if ('title', 'イギリス') in data.items():
print(data)
躓いたあれこれ
gzipファイル
最初はgzipファイルを展開して、JSONファイルとして処理していましたが、
調べていたらgzipのまま読み込んで処理できることが発覚...色々できるのね
import gzip
with gzip.open('wiki_data/jawiki-country.json.gz') as f:
for line in f:
<略>
json.decoder.JSONDecodeError: Extra data: line 2 column 1
エラーが発生したJSON読み込みコードは以下です。
with open('wiki_data/jawiki-country.json') as f:
data = json.load(f)
今回のファイルは以下のように複数のJSONがつながっているようなデータでした。
なのでファイルごと読み込もうとしても、読めないと怒られてしまいました。
{"text": "#転送 [[アイルランド共和国]]", "title": "アイルランド"}
{"text": "{{基礎情報 ...<長いので略>...[[Category:オセット]]", "title": "南オセチア"}
:
略
:
そのため、開いたファイルから一行ずつデータを読み込み
JSONとしてとりこんでいます。
with open('wiki_data/jawiki-country.json', encoding='utf-8') as f:
for line in f:
data = json.loads(line)
json.loadとjson.loadsの違い
上のエラーの際、JSONの読み込みにjson.load
を使っていますが、
最終的なコマンドではjson.loads
を使っています。
違いは以下でした。
- load関数→ファイルオブジェクトからPythonオブジェクトに変換する
- loads関数→文字列からPythonオブジェクトに変換する
なので、今回は読み込むファイルが単一のJSONで作られていれば、読みこたということでした。
{"text": "#転送 [[アイルランド共和国]]", "title": "アイルランド"}
with open('test.json') as f:
data = json.load(f)
一行ずつ読み込むときは文字列として読んでいるため、json.loads,で読み込むことができるというわけでした。
JSONの表記が意外と厳格
これは調べていくうちに見つけた内容です。
Pythonは結構適当な(?)ところがあり、たとえばシングルクオートやダブルクオートがどっちでもいいように解釈してくれたりしてくれますが、
JSONデータの場合はそうも行かないようです。
以下が例です。
>>> json.loads('{"a": 1}') # これはは読み込める
{'a': 1}
>>> json.loads("{'a': 1}") # これはエラーとなる
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes
ファイルから直接読めば問題ないですが、
ファイル処理の中で、ダブルクオートがシングルクオートになっちゃったりするとエラーがでます。
>>> a = '{"a": 1}'
>>> a
'{"a": 1}'
>>> json.loads(a) # これは読み込める
{'a': 1} # シングルクオートになっている
>>> json_data = json.loads(a) # json_dataに一度格納
>>> type(json_data)
<class 'dict'>
>>> json.loads(json_data) # dictじゃ読めない
TypeError: the JSON object must be str, bytes or bytearray, not dict
>>> json.loads(str(json_data)) # strにして読み込むとシングルクオートで読めない
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
なので、JSONデータを扱うときは
json.daumpsを使ってJSON形式でちゃんと出力されるようにする必要があるみたいです。
>>> json_data
{'a': 1}
>>> json.dumps(json_data)
'{"a": 1}' # ダブルクオートで出力してくれる
最後に
第3章に突入しました。
JSONは最近扱い始めたので、ここでしっかり身につけたいです。