開発言語 Python(その19)

【連載】

にわか管理者のためのLinux運用入門

【第180回】開発言語 Python(その19)

[2019/05/29 07:00]後藤大地 ブックマーク ブックマーク

サーバ/ストレージ

小さいツールを組み合わせる

これまで2回に渡ってPythonを使って実用的なツールを作成する方法を紹介してきた。1つはCSVデータをパースして処理する方法、もう1つはJSONデータのパースやダンプの方法だ。それ専用にモジュールが用意されているので、その機能を使って操作すればよい。どちらも簡単なサンプルだった。

例えば、次のサンプルコード(csv_read.py)は、CSVデータをファイルから読み込んでパースし、行ごとに(セルごとにとも言える)処理するといった内容になっている。

#!/usr/bin/env python3

import csv

with open('addresses.csv') as fp:
    reader = csv.reader(fp)
    for row in reader:
        for i in range(0,4):
            print('[' + row[i] + ']', end='')
        print()

また、次のサンプル(genjson2.py)はPythonのディクショナリとして用意したデータをJSON形式の文字列に変換して出力するというものだ。

#!/usr/bin/env python3

import json

# JSONに相当するデータをディクショナリで用意
jsondic = {"status":"success","data":{"ipv4":"93.184.216.34","continent_name":"North America","country_name":"United States","subdivision_1_name":"Massachusetts","subdivision_2_name":None,"city_name":"Norwell","latitude":"42.15080","longitude":"-70.82280"}}

# ディクショナリデータを形式指定なしでJSON文字列として出力
print('----')
print(json.dumps(jsondic))

# ディクショナリデータをインデント指定してJSON文字列として出力
print('----')
print(json.dumps(jsondic, indent=4))

どちらも簡単なサンプルだが、これらのサンプルを組み合わせることでさらに別のツールが作れることにお気づきだろうか。Pythonはかなり短いコードで機能的に動作するツールを作成することができるのだ。

CSVデータをJSONデータに変換して出力するコマンド

今回は上記2つのツールを組み合わせて、CSVデータをJSONデータに変換して出力するコマンド(csv2json.py)を作ってみよう。まず、CSVを操作するツールを作成した際に用いた住所データにヘッダを追加し、次のようなCSVデータ「addresses.csv」を用意する。

"全国地方公共団体コード","(旧)郵便番号","郵便番号","都道府県名","市区町村名","町域名","都道府県名","市区町村名","町域名","一町域が二以上の郵便番号で表される場合の表示","小字毎に番地が起番されている町域の表示","丁目を有する町域の場合の表示","一つの郵便番号で二以上の町域を表す場合の表示","更新の表示","変更理由"
13101,"100  ","1000000","トウキョウト","チヨダク","イカニケイサイガナイバアイ","東京都","千代田区","以下に掲載がない場合",0,0,0,0,0,0
13101,"102  ","1020072","トウキョウト","チヨダク","イイダバシ","東京都","千代田区","飯田橋",0,0,1,0,0,0
13101,"102  ","1020082","トウキョウト","チヨダク","イチバンチョウ","東京都","千代田区","一番町",0,0,0,0,0,0
13101,"101  ","1010032","トウキョウト","チヨダク","イワモトチョウ","東京都","千代田区","岩本町",0,0,1,0,0,0
13101,"101  ","1010047","トウキョウト","チヨダク","ウチカンダ","東京都","千代田区","内神田",0,0,1,0,0,0
13101,"100  ","1000011","トウキョウト","チヨダク","ウチサイワイチョウ","東京都","千代田区","内幸町",0,0,1,0,0,0
13101,"100  ","1000004","トウキョウト","チヨダク","オオテマチ(ツギノビルヲノゾク)","東京都","千代田区","大手町(次のビルを除く)",0,0,1,0,0,0
13101,"100  ","1006890","トウキョウト","チヨダク","オオテマチジェイエイビル(チカイ・カイソウフメイ)","東京都","千代田区","大手町JAビル(地階・階層不明)",0,0,0,0,0,0
13101,"100  ","1006801","トウキョウト","チヨダク","オオテマチジェイエイビル(1カイ)","東京都","千代田区","大手町JAビル(1階)",0,0,0,0,0,0
13101,"100  ","1006802","トウキョウト","チヨダク","オオテマチジェイエイビル(2カイ)","東京都","千代田区","大手町JAビル(2階)",0,0,0,0,0,0
13101,"100  ","1006803","トウキョウト","チヨダク","オオテマチジェイエイビル(3カイ)","東京都","千代田区","大手町JAビル(3階)",0,0,0,0,0,0
13101,"100  ","1006804","トウキョウト","チヨダク","オオテマチジェイエイビル(4カイ)","東京都","千代田区","大手町JAビル(4階)",0,0,0,0,0,0
13101,"100  ","1006805","トウキョウト","チヨダク","オオテマチジェイエイビル(5カイ)","東京都","千代田区","大手町JAビル(5階)",0,0,0,0,0,0
13101,"100  ","1006806","トウキョウト","チヨダク","オオテマチジェイエイビル(6カイ)","東京都","千代田区","大手町JAビル(6階)",0,0,0,0,0,0
13101,"100  ","1006807","トウキョウト","チヨダク","オオテマチジェイエイビル(7カイ)","東京都","千代田区","大手町JAビル(7階)",0,0,0,0,0,0
13101,"100  ","1006808","トウキョウト","チヨダク","オオテマチジェイエイビル(8カイ)","東京都","千代田区","大手町JAビル(8階)",0,0,0,0,0,0
13101,"100  ","1006809","トウキョウト","チヨダク","オオテマチジェイエイビル(9カイ)","東京都","千代田区","大手町JAビル(9階)",0,0,0,0,0,0
13101,"100  ","1006810","トウキョウト","チヨダク","オオテマチジェイエイビル(10カイ)","東京都","千代田区","大手町JAビル(10階)",0,0,0,0,0,0
13101,"100  ","1006811","トウキョウト","チヨダク","オオテマチジェイエイビル(11カイ)","東京都","千代田区","大手町JAビル(11階)",0,0,0,0,0,0
13101,"100  ","1006812","トウキョウト","チヨダク","オオテマチジェイエイビル(12カイ)","東京都","千代田区","大手町JAビル(12階)",0,0,0,0,0,0

日本郵便が提供している郵便番号データ(CSV)にはヘッダは含まれていない。ただし、説明文章にはヘッダに相当するものが説明されており、その説明文言を1行目に書き込んだものが上記データである。

この1行目をJSONデータの鍵とし、1行目以外を値としてJSONデータを作成するコードを考える。作成はそれほど難しくない。次に示すcsv2json.pyのように、CSVデータをパースしながらディクショナリおよびリストデータを作成して行き、最後にjson.dumps()で文字列に変換して出力すればOKだ。

#!/usr/bin/env python3

import sys
import csv
import json

# 住所データを保持するリスト
jsonlist = []

with open(sys.argv[1]) as fp_r:
    # 1行づつ読み込んで処理
    reader = csv.reader(fp_r)

    # 最初の1行目はヘッダデータとして処理する
    header = {}
    is_header = True

    for row in reader:
        if is_header:
            # 1行目をヘッダとして確保
            header = row
            is_header = False
        else:
            # 2行目以降はJSONディクショナリデータとして確保し
            # リストへ追加
            jsondic = {}
            for i in range(len(header)):
                jsondic[header[i]] = row[i]
            jsonlist.append(jsondic)

print(json.dumps(jsonlist, ensure_ascii=False, indent=4))

このコマンドを実行すると次のような結果を得ることができる。

% ./csv2json.py addresses.csv 
[
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "100  ",
        "郵便番号": "1000000",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "以下に掲載がない場合",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "0",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "102  ",
        "郵便番号": "1020072",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "飯田橋",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "1",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "102  ",
        "郵便番号": "1020082",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "一番町",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "0",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "101  ",
        "郵便番号": "1010032",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "岩本町",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "1",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "101  ",
        "郵便番号": "1010047",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "内神田",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "1",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "100  ",
        "郵便番号": "1000011",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "内幸町",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "1",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "100  ",
        "郵便番号": "1000004",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "大手町(次のビルを除く)",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "1",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "100  ",
        "郵便番号": "1006890",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "大手町JAビル(地階・階層不明)",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "0",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "100  ",
        "郵便番号": "1006801",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "大手町JAビル(1階)",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "0",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "100  ",
        "郵便番号": "1006802",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "大手町JAビル(2階)",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "0",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "100  ",
        "郵便番号": "1006803",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "大手町JAビル(3階)",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "0",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "100  ",
        "郵便番号": "1006804",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "大手町JAビル(4階)",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "0",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "100  ",
        "郵便番号": "1006805",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "大手町JAビル(5階)",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "0",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "100  ",
        "郵便番号": "1006806",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "大手町JAビル(6階)",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "0",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "100  ",
        "郵便番号": "1006807",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "大手町JAビル(7階)",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "0",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "100  ",
        "郵便番号": "1006808",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "大手町JAビル(8階)",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "0",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "100  ",
        "郵便番号": "1006809",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "大手町JAビル(9階)",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "0",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "100  ",
        "郵便番号": "1006810",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "大手町JAビル(10階)",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "0",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "100  ",
        "郵便番号": "1006811",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "大手町JAビル(11階)",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "0",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    },
    {
        "全国地方公共団体コード": "13101",
        "(旧)郵便番号": "100  ",
        "郵便番号": "1006812",
        "都道府県名": "東京都",
        "市区町村名": "千代田区",
        "町域名": "大手町JAビル(12階)",
        "一町域が二以上の郵便番号で表される場合の表示": "0",
        "小字毎に番地が起番されている町域の表示": "0",
        "丁目を有する町域の場合の表示": "0",
        "一つの郵便番号で二以上の町域を表す場合の表示": "0",
        "更新の表示": "0",
        "変更理由": "0"
    }
]
%

ご覧のように、CSVデータがJSONデータに変換されていることを確認できる。このコードは結構汎用性があり、CSVで同じ形式のデータを用意してあれば、それをここで説明したようなJSON形式に変換することが可能だ。

こんな感じでPythonでは簡単に実用的なツールを作成することができる。C言語で作ればもっと高速に動作するものを作成することもできるが、コードの量は増えるし、Pythonと比べると理解しづらい記述になってしまうところがある。何度も言ってきたが、Pythonは学習が容易で、費用対効果の高いプログラミング言語だ。ぜひともこれまで3回にわたって取り上げたツールなどを実際に試し、Pythonプログラミングを体験していただきたい。

※ 本記事は掲載時点の情報であり、最新のものとは異なる場合がございます。予めご了承ください。

一覧はこちら

連載目次

もっと知りたい!こちらもオススメ

なぜ今、統合システムなのか? 押さえておくべき「3つのインパクト」

なぜ今、統合システムなのか? 押さえておくべき「3つのインパクト」

ガートナー ジャパンは10月31日~11月2日、都内で「Gartner Symposium/ITxpo 2017」を開催。11月1日には同社 主席アナリストの青山浩子氏が登壇し「CIOが理解すべき統合システムの3大インパクト」と題する講演を行った。本稿では、講演の内容をダイジェストでお届けする。

関連リンク

この記事に興味を持ったら"いいね!"を Click
Facebook で IT Search+ の人気記事をお届けします

会員登録(無料)

注目の特集/連載
[解説動画] Googleアナリティクス分析&活用講座 - Webサイト改善の正しい考え方
知りたい! カナコさん 皆で話そうAIのコト
教えてカナコさん! これならわかるAI入門
対話システムをつくろう! Python超入門
Kubernetes入門
AWSで作るクラウドネイティブアプリケーションの基本
ソフトウェア開発自動化入門
PowerShell Core入門
徹底研究! ハイブリッドクラウド
マイナビニュース スペシャルセミナー 講演レポート/当日講演資料 まとめ
セキュリティアワード特設ページ

一覧はこちら

今注目のIT用語の意味を事典でチェック!

一覧はこちら

ページの先頭に戻る