Posted at

Pythonによるスクレイピング&機械学習のお勉強その3-1 - Webのデータはさまざまなデータフォーマット

More than 1 year has passed since last update.


今回の目標

このシリーズでは教科書(文献1)に沿ってPythonによるスクレイピングと機械学習を学びます。今回は第3章「データソースと書式・整形」から3-1「Webのデータはさまざまなデータフォーマット」を学びます。

原則、教科書のサンプルプログラムを作成してゆきますが、著作権に配慮し、できるだけそのままではなく類題を作成して勉強してゆく方針です。


方法と結果


  • 準備

その0で作成した学習用docker環境でpythonの実行を行います。

$ docker run -t -i -v $HOME/src:$HOME/src pylearn2 /bin/bash


例題3-1-1テキストデータとバイナリデータ

まずは、バイナリデータの書き込みの例題です。非常にシンプルなサンプルコードなので、ほぼそのまま掲載します。


write100.py

#!/usr/bin/env python

# -*- coding: utf-8 -*-

# ファイル名とデータ
filename = 'a.bin'
data = 100

# 書き込み
with open(filename, 'wb') as f:
f.write(bytearray([data]))


これでa.binファイルに0x64の1バイトのデータが書き込まれました。


類題3-1-2 XMLの解析

教科書では横浜市が公開している防災関連データから固定のXMLをダウンロードして、タグを解析する例題が紹介されています。今回は同じ横浜市から、津波避難施設のデータ(ZIPで圧縮されたXMLデータ)をダウンロードして解析する類題を行います。


xml-hinan-cclef.py

#!/usr/bin/env python

# -*- coding: utf-8 -*-

import pylearn as pl
import os.path
import io
from zipfile import ZipFile
import random
from bs4 import BeautifulSoup as bs

zipurl = 'http://www.city.yokohama.lg.jp/somu/org/kikikanri/data/tsunamievacuationfacilitylist.zip'

def dl_and_extract():
print('zipファイルをダウンロードします')
zip = pl.download(zipurl, {}, False)
with ZipFile(io.BytesIO(zip), mode='r') as zf:
zf.extractall()

# すでにダウンロードされたファイルがあるかをチェック
xmlfile = 'TsunamiEvacuationFacilityList.xml'
exist = os.path.exists(xmlfile)

# ダウンロードされていなければ新たにダウンロード
if not exist:
dl_and_extract()
else:
print('xmlファイルはダウンロード済のため、ローカルから読み込みます。')

# ローカルからxmlを読み込む
with open(xmlfile, 'r') as f:

xml = f.read()

# soup
soup = bs(xml, 'html.parser')

# 名称のリストを取得
# 小文字として扱われるらしい
names = soup.select('tsunamievacuationfacilitylist > tsunamievacuationfacility > name')

# 件数を表示
print('津波避難施設は', len(names), '件見つかりました。')

# ランダムに1つ表示
print('ランダムに1施設:', random.choice(names).string)



  • 実行結果

$ python ./xml-hinan-cclef.py

xmlファイルはダウンロード済のため、ローカルから読み込みます。
津波避難施設は 172 件見つかりました。
ランダムに1施設: 港中学校


例題3-1-3 JSONの解析

教科書では著者が運営されているクジラWeb APIから百人一首のデータをJSONでダウンロードする例題が解説されていましたが、2-4節でもJSONは扱ったのでパスします。


類題3-1-4 YAMLの解析

教科書ではYAMLの基本的な読み書きがサンプルコードとして提示されていました。ここでは類題として、類題2-4でも扱った仮想通貨のリストをJSON形式からYAML形式に変換するというタスクを扱います。

(注: 一般的なJSON to YAML変換を実装しているわけではなく、単純なJSONデータ構造に依存した、実用性には乏しい練習用コードです)


J2Y-cclef.py

#!/usr/bin/env python

# -*- coding: utf-8 -*-

import pylearn as pl
import yaml
import json
import os.path
import random

# API
API = 'https://www.cryptopia.co.nz/api/GetCurrencies'

# JSONファイル名
JSONFILE = 'currencies.json'

# YAMLファイル名
YAMLFILE = 'currencies.yaml'

# JSONファイルがすでに存在するかチェックする
jsonexists = os.path.exists(JSONFILE)

# 存在しなければダウンロードする
if not jsonexists:
js = pl.download(API)
with open(JSONFILE, 'w') as jf:
jf.write(js)
print(JSONFILE, 'を作成しました。')
else:
print('JSONファイルはすでに存在しているためダウンロードしません。')

# JSONデータを読み込む
data = None
with open(JSONFILE, 'r') as jf:
data = json.load(jf)

# YAMLコンテンツを作成
yamldata = ''

# Success節を作成
yamldata += 'Success: '
if data['Success'] is True:
yamldata += 'true'
else:
yamldata += 'false'
yamldata += '\n'

# Message節を作成
yamldata += 'Message: '
if data['Message'] is None:
yamldata += ''
else:
yamldata += data['Message']
yamldata += '\n'

# Data節を作成
yamldata += 'Data: \n'

currencies = data['Data']
for currency in currencies:
yamldata += ' - Id: '
yamldata += str(currency['Id'])
yamldata += '\n'

yamldata += ' Name: '
yamldata += currency['Name']
yamldata += '\n'

yamldata += ' Symbol: '
yamldata += currency['Symbol']
yamldata += '\n'

yamldata += ' Algorithm: '
yamldata += currency['Algorithm']
yamldata += '\n'

#他にもあるが、省略

# YAMLを書き出し
with open(YAMLFILE, 'w') as yf:
yf.write(yamldata)

# YAMLを解析
ydata = yaml.load(yamldata)
ycurrencies = ydata['Data']

print('YAML内に', len(ycurrencies), '個の通貨情報があります。')
# ランダムに通貨を一つ表示
ycur = random.choice(ycurrencies)
print('通貨名: ', ycur['Name'])
print('シンボル: ', ycur['Symbol'])
print('アルゴリズム: ', ycur['Algorithm'])



  • 実行結果

$ python ./J2Y-cclef.py

currencies.json を作成しました。
YAML内に 589 個の通貨情報があります。
通貨名: BirdCoin
シンボル: BIRD
アルゴリズム: Scrypt
$ head currencies.yaml
Success: true
Message:
Data:
- Id: 762
Name: $PAC
Symbol: $PAC
Algorithm: X11
- Id: 573
Name: 21Million
Symbol: 21M

このような感じでyamlファイルが作られています。


類題3-1-5 CSV/TSVの解析

教科書ではcodecsモジュールを使ってSJIS形式のファイルを読み書きする方法、csvモジュールを使ってCSVファイルを読み書きする簡単な方法がサンプルとして記載されていました。今回の類題では類題3-1-4で用いた仮想通貨リストのJSONファイルをCSVに変換してみます。


J2C-cclef.py

#!/usr/bin/env python

# -*- coding: utf-8 -*-

import pylearn as pl
import csv
import codecs
import json
import os.path
import random

# API
API = 'https://www.cryptopia.co.nz/api/GetCurrencies'

# JSONファイル名
JSONFILE = 'currencies.json'

# CSVファイル名
CSVFILE = 'currencies.csv'

# JSONファイルがすでに存在するかチェックする
jsonexists = os.path.exists(JSONFILE)

# 存在しなければダウンロードする
if not jsonexists:
js = pl.download(API)
with open(JSONFILE, 'w') as jf:
jf.write(js)
print(JSONFILE, 'を作成しました。')
else:
print('JSONファイルはすでに存在しているためダウンロードしません。')

# JSONデータを読み込む
data = None
with open(JSONFILE, 'r') as jf:
data = json.load(jf)

headers = ['Id', 'Name', 'Symbol', 'Algorithm', 'WithdrawFee', 'MinWithdraw’, MaxWithdraw', 'MinBaseTrade',
'IsTipEnabled', 'MinTip', 'DepositConfirmations', 'Status', 'StatusMessage',
'ListingStatus']

currencies = data['Data']

# CSVファイルを書き出す
with codecs.open(CSVFILE, 'w', 'shift_jis') as fp:
writer = csv.writer(fp, delimiter=',', quotechar='"')
#タイトル行を書き出す
writer.writerow(headers)
for currency in currencies:
row = []
for header in headers:
row.append(currency[header])
writer.writerow(row)

print('正常にCSV書き込みが終了しました。')



  • 実行結果

$ python ./J2C-cclef.py

JSONファイルはすでに存在しているためダウンロードしません。
正常にCSV書き込みが終了しました
$ head currencies.csv
Id,Name,Symbol,Algorithm,WithdrawFee,MinWithdraw,MaxWithdraw,MinBaseTrade,IsTipEnabled,MinTip,DepositConfirmations,Status,StatusMessage,ListingStatus
762,$PAC,$PAC,X11,2e-05,0.0001,20000000.0,2e-05,False,0.0,20,OK,,Active
573,21Million,21M,None,7.0,14.0,20000000.0,2e-05,False,1.0,20,OK,,Delisting
562,300 Token,300,None,0.006,0.012,20000000.0,2e-05,False,0.0,20,OK,,Active
427,42-coin,42,Scrypt,2e-08,2e-07,2000000000.0,2e-05,False,0.0,200,OK,,Active
339,808,808,SHA256,0.01,0.02,2000000000.0,2e-05,False,5.0,20,OK,,Active
108,8Bit,8BIT,POS,1.0,2.0,2000000000.0,2e-05,False,0.0,200,OK,,Active
759,AC3,AC3,X11,0.01,0.02,20000000.0,2e-05,False,0.0,20,OK,,Active
285,ACoin,ACOIN,SHA256,0.0001,0.0004,2000000000.0,2e-05,False,0.0,20,OK,,Active
581,Adcoin,ACC,Scrypt,0.01,0.02,20000000.0,2e-05,False,0.0,20,OK,,Active

小さな数値の扱いで若干表記のおかしいところがありますが、細かい点を除いてはCSVに変換することができました。


類題3-1-6 Excelファイルの解析

ここではPythonでMicrosoft Excelファイルのデータ形式を扱う練習をします。まず準備としてPython-Excelというライブラリをインストールします。

$ pip install openpyxl

Collecting openpyxl
Downloading https://files.pythonhosted.org/packages/f6/13/3c1263b852377738eaa60f99602fb58cc8ad2fd1badb0b724b0d5b532727/openpyxl-2.5.4.tar.gz (170kB)
100% |████████████████████████████████| 174kB 1.3MB/s
Collecting jdcal (from openpyxl)
Downloading https://files.pythonhosted.org/packages/a0/38/dcf83532480f25284f3ef13f8ed63e03c58a65c9d3ba2a6a894ed9497207/jdcal-1.4-py2.py3-none-any.whl
Collecting et_xmlfile (from openpyxl)
Downloading https://files.pythonhosted.org/packages/22/28/a99c42aea746e18382ad9fb36f64c1c1f04216f41797f2f0fa567da11388/et_xmlfile-1.0.1.tar.gz
Building wheels for collected packages: openpyxl, et-xmlfile
Running setup.py bdist_wheel for openpyxl ... done
Stored in directory: /root/.cache/pip/wheels/d3/75/83/415a5bbed8366f66a87f7bd0fc28f7df6e13982960f3e5a7ab
Running setup.py bdist_wheel for et-xmlfile ... done
Stored in directory: /root/.cache/pip/wheels/2a/77/35/0da0965a057698121fc7d8c5a7a9955cdbfb3cc4e2423cad39
Successfully built openpyxl et-xmlfile
Installing collected packages: jdcal, et-xmlfile, openpyxl
Successfully installed et-xmlfile-1.0.1 jdcal-1.4 openpyxl-2.5.4

また、PandasライブラリでExcelファイルを扱うサンプルコードも載っています。Pandas自体はminicondaに最初から搭載されているので問題ないのですが、Excelサポートにはxlrdというライブラリが必要だと怒られてしまったので、インストールします。

$ pip install xlrd

Collecting xlrd
Downloading https://files.pythonhosted.org/packages/07/e6/e95c4eec6221bfd8528bcc4ea252a850bffcc4be88ebc367e23a1a84b0bb/xlrd-1.1.0-py2.py3-none-any.whl (108kB)
100% |████████████████████████████████| 112kB 364kB/s
Installing collected packages: xlrd
Successfully installed xlrd-1.1.0

教科書では都道府県の人口データが掲載された比較的単純なExcelファイルの読み書きを両ライブラリで行うサンプルコードが記載されていました。

今回は類題として、類題3-1-4から使っている仮想通貨のJSON形式リストをExcelファイルに書き出す練習をします。

通常はどちらかのライブラリを使うのだと思いますが、今回は練習のため書き込みはopenpyxl、確認用の読み込みはPandasを使ってみます。Pandasはどうも読み込みのみの対応のようです。


J2E-cclef.py

#!/usr/bin/env python

# -*- coding: utf-8 -*-

import pylearn as pl
import json
import os.path
import random
import openpyxl
import pandas

# API
API = 'https://www.cryptopia.co.nz/api/GetCurrencies'

# JSONファイル名
JSONFILE = 'currencies.json'

# Excelファイル名
EXCELFILE = 'currencies.xlsx'

# JSONファイルがすでに存在するかチェックする
jsonexists = os.path.exists(JSONFILE)

# 存在しなければダウンロードする
if not jsonexists:
js = pl.download(API)
with open(JSONFILE, 'w') as jf:
jf.write(js)
print(JSONFILE, 'を作成しました。')
else:
print('JSONファイルはすでに存在しているためダウンロードしません。')

# JSONデータを読み込む
data = None
with open(JSONFILE, 'r') as jf:
data = json.load(jf)

headers = ['Id', 'Name', 'Symbol', 'Algorithm', 'WithdrawFee', 'MinWithdraw', 'MaxWithdraw',
'MinBaseTrade', 'IsTipEnabled', 'MinTip', 'DepositConfirmations', 'Status',
'StatusMessage', 'ListingStatus']

currencies = data['Data']

# Excelファイルを書き出す
book = openpyxl.Workbook()
sheet = book.active

sheet.append(headers)
for currency in currencies:
sheet.append(list(currency.values()))

book.save(EXCELFILE)
print('OpenPyxlによるExcelファイルの書き込みが正常終了しました:', EXCELFILE)

# Pandasによる読み込み
book = pandas.read_excel(EXCELFILE)
print(book)



  • 実行結果

$ python ./J2E-cclef.py

JSONファイルはすでに存在しているためダウンロードしません。
OpenPyxlによるExcelファイルの書き込みが正常終了しました: currencies.xlsx
Id ... ListingStatus
0 762 ... Active
1 573 ... Delisting
2 562 ... Active
3 427 ... Active
#(後略)

これで仮想通貨リストのJSONデータをxlsx形式に変換できました。


今回達成したこと


  • バイナリデータのファイル書き込みの基本を学びました。

  • XML, JSON, YAML, CSV, Excelデータの読み書きの基本を学びました。

今回は扱うデータ形式がたくさんあったので大変でした。


参考文献


  1. クジラ飛行机, Pythonによるスクレイピング&機械学習[開発テクニック], ソシム株式会社, 2016

  2. PyYAML documentation

  3. OpenPyxl documentation

  4. Pandas API documentation