Help us understand the problem. What is going on with this article?

僕はプログラミングができません⑤ 〜言語処理100本ノック No.20〜

はじめに

言語処理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では,ここで抽出した記事本文に対して実行せよ.

実行コマンド

20.py
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で作られていれば、読みこたということでした。

test.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は最近扱い始めたので、ここでしっかり身につけたいです。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away