はじめに
EDINETという金融庁のページから有報のデータが取れるらしいということで、実際にやってみています。まだ道半ばではあるのですが、ある程度出来てきたのでどんなことをやったらEDINETから有報のデータが取れるのかということを記事にしました。
この記事では具体的な実装方法については述べません。「大体こんなことをしたら有報のデータを取得できるんだ」ということがわかるようにしています。
わたしはpythonをつかって実装していますが
- httpリクエスト
- xml文書の解析
- jsonの読み込み
- データベース
のためのライブラリがあれば別の言語でも問題なく実装できると思います。
関連記事
EDINETのXBRLを分析した結果を公開しています。
PythonでEDINETのXBRLを分析する記事を書いています。
EDINETを利用する上での注意点
サーバーに大きな負荷をかけないようにしてください。1リクエストあたり数秒の間隔をあければ大丈夫だと思います。
1.EDINET上にどんな文書があるかを調べる
EDINETでは開示された文書を取得するためのweb APIを提供しており、これをつかってXBRLを取得します。具体的には特定のURLに対してhttpリクエストを投げると結果が返ってくるみたいな使い方をします。EDINET APIの仕様書はここにあります。
使ってみるとわかるのですが結構不便なAPIで、ある銘柄のXBRLの一覧を取得するとかそういった横断的な検索ができません。
EDINET APIを使って出来るのは
- 特定の日、例えば2022年8月19日に提出された文書の一覧を取得する(文書IDや種別、説明などの一覧)
- 文書IDから文書、ここではXBRLを1つダウンロードする
ことだけです。
そのため、EDINET APIの1つ目の機能を使って、EDINETにどんな文書が存在しているのかを調べる必要があります。EDINET上の文書の保管期限は5年であるため、5年前の特定の年月日から現時点までのすべての日についてEDINETに問い合わせ、文書のデータベースを作成します。
EDINETに問い合わせると以下のようなJSON文書が返されます。
{
"metadata": {
"title": "タイトル",
"parameter": {
"date": "2017-06-15",
"type": "2"
},
"resultset": {
"count": 213
},
"processDateTime": "2022-05-29 00:01",
"status": "200",
"message": "OK"
},
"results": [
{
"seqNumber": 1,
"docID": "S100A79S",
"edinetCode": "E11764",
"secCode": null,
"JCN": "1010401064261",
"filerName": "提出者",
"fundCode": "G03038",
"ordinanceCode": "030",
"formCode": "07A000",
"docTypeCode": "120",
"periodStart": "2016-03-16",
"periodEnd": "2017-03-15",
"submitDateTime": "2017-06-15 09:00",
"docDescription": "文書の説明",
"issuerEdinetCode": null,
"subjectEdinetCode": null,
"subsidiaryEdinetCode": null,
"currentReportReason": null,
"parentDocID": null,
"opeDateTime": null,
"withdrawalStatus": "0",
"docInfoEditStatus": "0",
"disclosureStatus": "0",
"xbrlFlag": "1",
"pdfFlag": "1",
"attachDocFlag": "1",
"englishDocFlag": "0"
},
{
"seqNumber": 2,
"docID": "S100A5WQ",
(以下くりかえし)
metadataには「このJSON文書はどんなものか」といった情報が記載されています。具体的には
- JSON文書のタイトル
- どんなパラメータに対して返されたJSON文書か
- いくつのデータが存在するか
- 処理時間
- ステータス
- メッセージ
です。
また、それ以降のresultsに文書の情報が記載されています。例えば
- docID(EDINET上に存在する文書の一意な識別子)
- edinetCode(文書提出者のEDINET CODE)
- filerName(提出者名)
- docDescription(文書の説明)
- xbrlFlag(文書に対応するXBRLが存在するか)
といった情報があります。
このresultsの情報を全てデータベースに保存します。レコード数が非常に多いのでcsvでの保存は非現実的です。特定の銘柄の有報の一覧を検索するのに1分くらいかかります。データベースだと3秒かかりません。データベースの使用を強くお勧めします。
ここら辺の処理については以下のページが詳しかったです。EDINET APIを用いたファイルのダウンロード処理についての説明もあります。非常に参考になります。おすすめです。
【EDINET API】Python で XBRL を取得する方法【決算分析】 | シラベルノート
2.EDINETから文書をダウンロードする
EDINET APIにリクエストを送って、XBRLをダウンロードします。このとき、上記で取得したdocID(EDINET上に存在する文書の一意な識別子)が必要です。
その都度、欲しいXBRLをダウンロードするのもよいのですが
- XBRLの解析はやや難解で時間がかかる(処理を実装するのが)
- EDINETの文書の保管期限が5年と決まっている
ため、入手可能なXBRLを全てダウンロードしてしまうことをお勧めします。
5年分のXBRLのファイル数は25万、ファイルサイズは120GB程度です。25万個のファイルをダウンロードするのにおよそ20日程度かかりました。
XBRLをダウンロードする際に注意しなくてはならないことはXBRLのZIPファイルをダウンロードできたとしても、それがZIPファイルとは限らないことです。わたしの場合は以下のパターンがありました。
- ファイルが見つからないことを示すJSON文書
- 通信混雑中、メンテナンス中であることを示すhtml文書
以下をチェックすることで今のところ問題なく処理できています。
- ZIPファイルを解凍できるかチェックする
- 解凍できるなら中にXBRLファイルが存在するかチェックする
- 解凍できないなら、それがJSON文書、html文書、それ以外(破損したZIP)かをチェックする
ちなみに最新5年分だと、破損したZIPファイルはありませんでした。また、XBRLフラグが立っているのにZIPファイルにXBRLが存在しないということもありませんでした。
やはり以下のページが非常に詳しく、参考になります。おすすめです。
【EDINET API】Python で XBRL を取得する方法【決算分析】 | シラベルノート
3.XBRLを解析する
1.有報の表示構造を取得する
ダウンロードしたXBRLのZIPファイルを解凍し、その中の表示リンクベースファイルを解析します。具体的には「jpcrp030000-asr-001_E00007-000_2022-03-31_01_2022-06-27_pre.xml」みたいなpre.xmlで終わるファイルを解析したらよいです。「ZIPファイルのどこにファイルがあるのか」「表示リンクベースファイルとは何か」についてはEDINETにタクソノミの仕様書があるのでそれを読んでください。
表示リンクベースファイルはツリー構造のファイルであり、以下のイメージのような情報を有しています。表示リンクベースファイルには「どこにどの要素が存在しているか」が書かれていますが「要素の値や中身(例えば数値は文章)」は書かれておらず、別のファイルを参照する必要があります。
「目次要素A」
| 「項目名」ーー「子項目」ーーー「孫項目」
| | 「子項目」
| |
| 「項目名」ーー「子項目」
|
|
「目次要素B」
(くりかえし続く)
語弊がありますが「目次要素」のいずれかが財務諸表、その下に連なる項目が財務諸表中の項目だと思ったらよいです。
結構簡単に見えますが、実際のファイルはxmlファイルでありもっと複雑です。以下が表示リンクベースファイルのxmlファイルとしての構造です。簡略化して書いています。
<目次要素A>
<目次要素B>
・・・
<目次要素Z>
<目次要素Aの構造>
<項目A>
<項目B>
<項目C>
<項目D>
・・・
<項目Z>
<項目Aは項目Bの3番目の子要素であることを示す要素>
<項目Hは項目Cの2番目の子要素であることを示す要素>
・・・
<目次要素Aの構造>
・・・
(くりかえし続く)
この表示リンクベースファイルを順序付きの木構造に読み込みます。たぶん、ここがいちばん面倒くさいです。
上記の図中には記載できていませんが、項目間の親子関係を表す要素には優先ラベルという属性があります。たとえば「preferredLabel="http://www.xbrl.org/2003/role/totalLabel"」といった値で子供の要素に係っています。この例ではtotalLabelと名の付く通り合計値ラベルを表しています。なぜこんなものが必要かといえば、要素に対応する日本語名称が複数存在するからです。
要素が損益計算書の場合
- 連結損益計算書
- 損益計算書
の2通りの日本語名称が存在し、表示リンクベース内の優先ラベルという属性に従って、いずれの日本語名称を表示するかを決定しています。
注意しなくてはならないのは、優先ラベル属性は
- 自身に設定されていれば自身の値
- 自身に設定されていなければ親の値(この直接の親だけでなく、更に親という風に再帰的に見ていく必要がある)
- 親に設定されていなければ標準ラベル
というような順位で各要素が使うものを決定します。木構造読み込み後に優先ラベル属性を親から子へ伝播させてもいいですし、後の日本語名称設定時に親の優先ラベルを再帰的に調べてもよいです。とにかく、自身の優先ラベルを調べるだけではだめだと覚えておいてください。
2.有報の各要素がなにかを調べる
さて、表示リンクベースファイルを解析することで「有報のどこになんという名前の要素があるか」がわかりました。しかし、各要素が何なのかはまだわかっていません。
例えば、財務諸表の要素であれば
- 「資産の部」のようなタイトル(文字要素)なのか
- 「現金または預金」のような数値なのか
- 表の横線のような取得する必要のない要素なのか
なのかを知る必要があります。
そのためにスキーマファイルを解析します。スキーマファイルはダウンロードしたZIPファイルにあるほかEDINETのサーバ上にも存在しています。
どのスキーマファイルを見ればよいかは要素の名称からわかります。要素の名称は「スキーマファイル名#要素名」という構造になっており、スキーマファイルを特定するのは簡単です。参考までに実際の要素の名称は例えば「http://disclosure.edinet-fsa.go.jp/taxonomy/jpcrp/2021-11-01/jpcrp_cor_2021-11-01.xsd#jpcrp_cor_InformationAboutGroupInformationAboutEmployeesAbstract」です。
以下はスキーマファイルのxmlファイルとしての構造です。簡略化して書いています。
<参照リンクベースファイル要素1>
<参照リンクベースファイル要素2>
・・・
<要素A 属性1の値、属性2の値、・・・・属性nの値>
<要素B 属性1の値、属性2の値、・・・・属性nの値>
<要素C 属性1の値、属性2の値、・・・・属性nの値>
(くりかえし続く)
<参照リンクベースファイル要素>はあとで使うので今は無視してください。ここでは<要素A 属性1の値、属性2の値、・・・・属性nの値>に着目します。
スキーマファイルを要素名で検索し、要素の属性値を取得します。あとはこの属性値に応じて、要素が何者なのかを判定すればよいだけです。例えば、type属性がstringItemでabstruct属性がtrueなら文字、abstruct属性がfalseなら数値などのように決まっています。ここに記載したのは適当なので実際のところはEDINETのタクソノミ仕様書を読んでください。
おわりに
すこし長くなったので、今回はここで終わります。これ以降は
- 各要素の日本語ラベルを取得する
- 各要素の数値を取得する
といった内容を書こうとおもっています。
次回の記事はこちらです。
www.quwechan.com
今回はここまでです。