zip ファイルを解凍せずに直接読み込む Python コード例を書きました。
EDINET も TDnet も EDGAR も、XBRL は『zip ファイル』で公開されていました。
zip ファイルの中身を読み込むときは、いったん HDD か SSD に解凍(展開)するのが一般的ですが、zip から直接読み込むこともできました。
やり方ですが、『標準の zipfile モジュール』を使用したらできました。
zip の中身を SSD とかに解凍しなくていいので、大量の XBRL を読み込むときに、省スペースで済みました。
とても便利です。
もちろん、XBRL 限らず、『普通の zip ファイル』から『普通のファイル』を狙って読み込むこともできました。
標準の zipfile モジュールを使う
Python で zip ファイルを読み込むときは、Python 標準の zipfile モジュールを使います。
これで zip ファイルを開いたら、中身のファイルリストを取得するメソッド『.infolist()』と、データを読み込むメソッド『.read()』を使用することができました。
これらを使います。
zipfile モジュールのマニュアル
zipfile モジュールの Python マニュアルの場所です。
zip ファイルを開くclass zipfile.ZipFile(file, mode='r', compression=ZIP_STORED, allowZip64=True)
中身のファイルリストを取得
ZipFile.infolist()
名前を指定して読み込む
ZipFile.read(name, pwd=None)
zip を読み込む
Python で zip を読み込むコード例です。
zip を解凍せずに中身を見るには、zipfile モジュールの ZipFile() を使います。
読み込み手順
拡張子『.xbrl』のファイルだけを読み込んで辞書に入れる例です。
zipfile.ZipFile() で zip ファイルを開いてから、zip_data.infolist() でファイルリストを取得します。
ファイルリストの要素には info.filename という属性がありますので、これをzip_data.read() に渡すと、データが読み込めます。
info.filename は、実際には以下のように『フォルダ構造を含んだファイルパス』でした。
'S100AM8K/XBRL/PublicDoc/jpcrp030000-asr-001_E00518-000_2017-03-31_01_2017-06-26.xbrl'
なので、ファイル名を使って読み込む・読み込まないを判定するときは、このファイルパスを意識して、判定部分を作ることになります。
コード例
まず、zipfile モジュールを使う部分です。
import zipfile
from collections import OrderedDict
import traceback
def read_file_from_zip(zip_file, re_match):
"""zipからファイル名を指定して読み込む"""
file_datas = OrderedDict()
try:
with zipfile.ZipFile(zip_file, 'r') as zip_data:
# ファイルリスト取得
infos = zip_data.infolist()
for info in infos:
# ファイルパスでスキップ判定
if re_match(info.filename) is None:
continue
# zipからファイルデータを読み込む
file_data = zip_data.read(info.filename)
# ファイルパスをキーにして辞書に入れる
file_datas[info.filename] = file_data
except zipfile.BadZipFile:
print(traceback.format_exc())
return file_datas
次に、この関数を使う部分です。
メイン関数です。
zip から読み込んだデータは、そのまま lxml.etree.fromstring() に渡すことができました。
データの中身はバイナリデータです。
import re
from lxml.etree import fromstring as etree_fromstring
def main():
"""メイン関数"""
# zipファイルの場所
zip_file = r'*****\S100AM8K.zip'
# 拡張子でファイルを選択してみる
re_match = re.compile(r'^.*?\.xbrl$').match
# zipからファイルデータを読み込む
files = read_file_from_zip(zip_file, re_match)
# 読み込んだXBRLファイルを1つずつ解析
for (file, data) in files.items():
# ファイル名
print('file: %s' % file)
# XML解析 lxml.etree
root = etree_fromstring(data)
print('root.tag: %s' % root.tag)
print('')
return
あと、メイン関数を呼び出すところです。
if __name__ == '__main__':
main()
zip から読み込んだ XML データを BeautifulSoup で読み込む場合です。
バイナリデータをそのまま渡せばOKでした。
単に文字列に変換したい場合は、適当なエンコーディングを指定して『.decode()』のメソッドを呼び出します。
バイトオーダーマーク (BOM) 付きの UTF-8 をデコードするときは、'utf-8-sig'
を指定します。'utf-8'
でデコードしたら、先頭にパイトオーダーマークの文字列が残ってしまいました。
from bs4 import BeautifulSoup
# XML解析 BeautifulSoup(HTMLパーサー)
soup = BeautifulSoup(data, features='lxml')
# XML解析 BeautifulSoup(XMLパーサー)
soup = BeautifulSoup(data, features='xml')
# バイナリをデコードして文字列に変換
text = data.decode('utf-8-sig')
zipfile.badzipfile エラー
たくさんの zip ファイルを読み込んでいたら、時々 zipfile.badzipfile エラーに遭遇しました。
これは、zip ファイルが壊れている時に出るようでした。
データは諦めるしかないのか?
実際のところ、あくまで『Python の zipfile で読めない』というだけですので、Python 以外の解凍ソフトで読み込んだら、部分的に解凍できたという場合も結構ありました。
それから、たまにですが、何度ダウンロードしても読み込みに失敗する zip ファイルも見かけました。
サーバーの zip ファイルが壊れたまま放置されてるケースですね。米国の EDGAE XBRL の zip で時々ありました。
とりあえず、zipfile.badzipfile が出たらログに記録だけして、読めない zip はスキップするようにしてます。
処理が終わったらログを見て、読めなかった zip を再取得したりする感じですね。
基本的には、そういう対応になると思います。
Arelle で XBRL ファイルを読み込む手順
zip の中の XBRL は、Arelle という Python ライブラリを使用したら、簡単に読み込むことができました。
zip から XBRL を読み込む手順です。
まず、zipfile.ZipFile() で zip ファイルを開きます。
つぎに、.infolist() で XBRL の zip 内パスを見つけます。
(例)\S100JKNH\XBRL\PublicDoc\jpcrp030000-asr-001_E00004-000_2020-05-31_01_2020-08-28.xbrl
それを、zip ファイルパスの後ろにくっつけます。
(例)F:\project\kabu\download\S100JKNH.zip\S100JKNH\XBRL\PublicDoc\jpcrp030000-asr-001_E00004-000_2020-05-31_01_2020-08-28.xbrl
これを Arelle に渡します。
これで、XBRL をパースすることができました。
Arelle は、『zip 内のほかのファイル』や『インターネット上のタクソノミ』を、自動的にたどってくれました。
なので、日本語ラベルなども、簡単に取得することができました。
Arelle の『インストール方法』や『使い方』の記事です。
⇒ 【Python】Arelle のインストール方法【XBRL 読み込みライブラリ】