この連載の読む順番
TDnet①〜④を終えてからEDINET①〜④へ進む構成です。共通するXBRLの基本操作はTDnet編を参照し、この回では取り出したEDINETの値を、出典を失わずに保存・比較する方法を扱います。
- TDnet解析① TDnetとは?決算短信・適時開示とEDINETの違い
- TDnet解析② XBRLとは?決算短信のPDFと数値データを図解
- TDnet解析③ XBRLの値を比較する前に、期間・単位・欠損を確かめる
- TDnet解析④ 累計と四半期単独、実績と予想を分けてグラフにする
- EDINET解析① EDINETとは?有価証券報告書から企業を理解する
- EDINET解析② XBRLとは?有価証券報告書の構造とタグの仕組みを理解する
- EDINET解析③ TDnetの次に読むXBRL――ファイル・タグ・コンテキストの違いを確かめる
- EDINET解析④ 財務データを継続的な分析に使う(この記事)
EDINET③では、公式サンプルの売上高を1件選び、期間と単位を確かめました。今回はその続きを考えます。1件の数値を画面に出せても、翌年の書類や訂正書類が来たとき、同じように比較できるとは限りません。「どの書類から」「どの条件で」取り出した値かも残しておく必要があります。
この記事では、まず保存の考え方を整理し、最後に同じ公式サンプル内の前期と当期の売上高をCSV・JSONに保存する小さな実習を行います。複数企業を自動で収集する仕組みや、あらゆる勘定科目を統合する処理は対象にしません。
数値は金融庁「2026年版EDINETタクソノミ」の公式サンプルです。実在企業の業績ではありません。仕様・サンプルの確認日:2026年10月27日。
1. 数字の横に、何を残しておくか
たとえば表計算ソフトに「2026年、売上高、323,609」とだけ保存すると、後から円なのか百万円なのか、連結なのか個別なのか分からなくなります。誤りに気づいても、元の書類へ戻れません。
そこで、値と一緒に値の意味を決める条件を残します。XBRLでは、一つの報告値をfact(ファクト)と呼びます。同じ売上高という項目でも、期間や対象範囲が違えば、別のfactとして扱います。
図は説明用の保存モデルです。EDINET APIのレスポンスや特定サービスの内部構造をそのまま表したものではありません。
最初は、次のように「書類についての情報」と「値についての情報」を分けると見通しが良くなります。
| 書類について残す情報 | 残す理由 |
|---|---|
書類管理番号(docID) |
EDINET上のどの提出書類かを特定する |
| 提出日時と取得日時 | 公表された時刻と、自分が取得した時刻を区別する |
| ZIP内のファイルパス | 本文・監査報告書など、実際に読んだファイルへ戻る |
| 元ファイルのハッシュ | 自分の手元のファイルが同じ内容かを確かめる |
| 解析プログラムの版 | 同じ入力でも、処理を変更した結果を区別する |
ハッシュは、ファイルの内容から計算する照合用の文字列です。この実習ではSHA-256を使います。ハッシュがあるだけで「金融庁が内容を認証した」ことにはなりません。自分が読んだファイルと、後で調べるファイルを照合するための目印です。
| 値について残す情報 | この回の例 |
|---|---|
| 企業・提出者を識別する値 | コンテキスト内のX99001-000
|
| 項目の識別子 | 名前空間URIとNetSales
|
| 期間 | 開始日・終了日。時点の値なら時点日 |
| 対象範囲 | 連結・個別、セグメントなどの条件 |
| 単位と精度 |
JPY、decimals="-6"
|
| 値と出典 | 元の数値、書類、ファイル、contextRef
|
contextRefの文字列だけを保存すれば十分、という意味ではありません。 CurrentYearDurationというIDは別の書類にも登場します。それぞれのファイルのcontextを参照し、実際の期間や条件を保存します。IDは「そのファイル内での参照先」です。
また、今回のX99001-000はサンプルのコンテキストから読んだ識別子です。書類一覧APIのedinetCodeや銘柄コードに無条件で置き換えたり、末尾を削って企業マスターに結合したりしません。識別子の体系を確認する工程は別に必要です。
2. タグは毎回調べるのか、それとも一つにまとめるのか
最初は「売上高という名前なら、同じ項目として一つにまとめればよい」と思うかもしれません。しかし、日本語ラベルは表示のための名前です。同じ表示名でも、違うタクソノミや提出者独自の項目である可能性があります。
項目の定義は、参照先を確認して再利用する
まず、インスタンスのschemaRefなどから参照されるスキーマをたどり、その書類が使っている定義の集合を把握します。必要な定義・リンクベースがどこにあるかは、EDINET解析②で扱った構造を使います。
取得・解析した定義は、参照URIや内容のハッシュとともに保存し、同じ内容を繰り返し読む際に再利用できます。毎回すべてをダウンロードし直す必要があるわけではありません。一方、URIが同じでも内容が変わっていないか、キャッシュをいつ確かめ直すかは運用で決めます。前回読んだものだから永久に同じ、とは扱いません。
項目の識別には、接頭辞だけでなく、次の組を使います。
名前空間URI
http://disclosure.edinet-fsa.go.jp/taxonomy/jppfs/2025-11-01/jppfs_cor
ローカル名
NetSales
jppfs_cor:NetSalesのjppfs_corは接頭辞です。接頭辞の文字が違っても同じURIを指す場合があり、逆に似た名前でもURIが異なれば同じ定義とは限りません。
定義を追加することと、意味を統合することを分ける
新しい項目が見つかったら、その定義を保存すること自体は自動化できます。ただし、既存の「売上高」の列へ自動で混ぜるかどうかは別の判断です。
| 起きたこと | 最初にすること |
|---|---|
| 同じURI・同じ項目名を再度見つけた | 保存済み定義との対応を確認し、値は別のfactとして保存 |
| タクソノミの版が変わった | 新しい定義を読み、変更点を確認 |
| 提出者独自のタグが増えた | 定義・ラベル・表示上の位置などを記録し、意味は未分類でも残す |
| 日本語ラベルが同じだった | 表示名だけで統合せず、定義・期間の種類・会計上の意味を調べる |
つまり、項目辞書は育てていけますが、辞書への登録と、分析用の項目への対応付けは別の処理です。未分類の項目を消してしまうより、未分類のまま出典付きで残す方が、後から判断できます。
3. 財務諸表の階層は、どうやって得るのか
「売上高の次に売上原価、その下に売上総利益を並べたい」という場合、factをXMLに出てきた順に並べるだけでは、帳票の構造を再現できません。表示上の関係は、表示リンクベースを確認します。
この連載でいう階層は、主に表示リンクに書かれた親子関係と順序です。財務上の意味や集計ルールのすべてを表すものではありません。
| 読む箇所 | 分かること |
|---|---|
presentationLinkのrole
|
どの表示関係のまとまりか |
locのlabelとhref
|
リンク内の目印が、どの項目を指すか |
presentationArcのfrom・to
|
親側・子側の目印 |
order |
同じ親の下での表示順序 |
preferredLabel |
その関係で使うラベルの役割が指定されているか |
たとえば、次のような関係を見つけたとします。以下は構造を読むための架空例です。
表示関係のまとまり:損益計算書の例
親A → 子B order=1
親A → 子C order=2
fromとtoに書かれた文字列を、そのまま勘定科目名だと思わないことがポイントです。先にlocで、その目印が参照する項目を解決します。参照が別ファイルなら、そのファイルも含めて確認します。
保存する際は「親・子・順序」だけでなく、**出典の書類と表示関係のまとまり(role)**も持たせます。同じ項目が異なる表に出ることがあり、すべてを一つの木に押し込むと、どの表の並びだったのかを失うためです。実際には複数の参照先やアークの属性を考慮する必要があり、この表だけで完全なXBRLプロセッサにはなりません。
表示リンクは「並べ方」を読む入口です。「親の値は子の合計」「上にある項目ほど重要」とは限りません。計算上の関係、単位、期間、注記は別に確かめます。表示リンクの公式資料の読み方と図は、EDINET解析②を参照してください。
4. 前年と比較する前に、「どの書類の前年か」を決める
継続的な分析で混同しやすいのが、対象となる年度と、その値が載っている書類の提出時点です。
現在の有価証券報告書には、当期だけでなく前期の比較値が載っていることがあります。これは便利ですが、前期の値を前年の書類から読んだ場合と、常に一致するとは限りません。会計方針の変更や訂正などによって、比較情報の扱いが変わる場合があるためです。
今回使うのはAです。一つの公式サンプルの中に載っている当期と前期比較値を取り出します。 過去に実在企業が公表した数値を再現する実習ではありません。
| 比較前の確認 | 確かめること |
|---|---|
| 項目 | 同じ名称だけでなく、定義や会計上の意味が比較可能か |
| 期間 | どちらも同程度の期間か。決算期変更などはないか |
| 範囲 | 連結と個別、全社と一部セグメントが混ざっていないか |
| 単位 | 円と百万円、通貨などがそろっているか |
| 出典の時点 | 当期書類の比較値なのか、当時の原公表値なのか |
| 注記 | 比較情報の修正、会計方針の変更などの説明がないか |
期間と単位が一致していても、事業の範囲や会計上の扱いまで同じとは限りません。最後は該当箇所の説明・注記も読みます。
Bのような「ある時点で知り得た情報」の分析には、その時点までの書類・取得履歴と、値の採用方針が必要です。現在取得できた最新の値だけから、過去の全時点を復元できるわけではありません。APIを利用する場合も、履歴の保持範囲と提供仕様を個別に確認します。
5. 訂正書類が来たとき、何を見直すか
EDINET①では訂正書類を探す入口を説明しました。ここでは、その情報を分析データの更新にどうつなげるかを考えます。
EDINET APIの書類一覧には、docIDに加え、親書類管理番号のparentDocIDがあります。公式仕様書の該当行では、項目名と項目IDを次のように確認できます。
出典:金融庁EDINET API仕様書 Version 2、2026年6月版、本文47頁(PDF52頁)、項目30。項目名・項目IDの部分を切り出して拡大しています。正式な説明・適用条件は原典を参照してください。
-
docID:今回の書類
今回確認している書類の識別子です。 -
parentDocID:親書類との関係
親書類との関係を調べる手掛かりです。 -
docTypeCode:書類の種類
訂正有価証券報告書などを区別します。 -
withdrawalStatus:取下げ
取下げに関する状態を確かめます。 -
docInfoEditStatus:書類情報の修正
財務局職員による書類情報修正に関する状態です。 -
disclosureStatus:不開示など
不開示などの状態を確かめます。
docTypeCodeでは、有価証券報告書が120、訂正有価証券報告書が130と定義されています(同仕様書の本文88頁)。一方、docInfoEditStatusは「財務数値が訂正されたか」を一括で判定するフラグではありません。書類の種類、親との関係、状態、実際の訂正内容を分けて読みます。
訂正を受け取ったら、いきなり既存の行をすべて上書きするのではなく、次の順番で影響を確認します。これは分析用の処理を考えるための例であり、EDINETがこの保存順序を指定しているわけではありません。
- 新しい書類の出典を保存する。 元の書類との関係や取得日時も残します。
- 訂正の対象を読む。 財務諸表の数値か、文章・注記か、書類情報の変更かを確認します。
- 影響する値と比較条件を特定する。 同じ期間でも、どの項目・範囲が変わったかを調べます。
- 採用する値と派生結果を更新する。 影響する表、増減率、グラフなどを計算し直します。
- 旧結果との対応を残す。 利用者が変更理由と出典に戻れるようにします。
親子関係が分かることと、「どの数値をどれに置き換えるべきか」が分かることは別です。本文だけの訂正も考えられますし、訂正書類に必要な数値がすべて入っていると決めつけることもできません。見つからない値をゼロにして更新しない、というTDnet③の注意点も引き続き当てはまります。
6. 実習:同じ公式サンプルから2期分を保存する
ここからは、EDINET③で用意したedinet-official-sample.zipとread_edinet_sample.pyを使います。Python・仮想環境・lxmlの準備はTDnet解析③、対象ファイルと名前空間の説明はEDINET③を参照してください。
今回も、対象は次の一つのファイルです。
jpcrp030000-asr-001_X99001-000_2026-03-31_01_2026-06-12.xbrl
確認した2件は次のとおりです。どちらも同じファイルのNetSalesです。
| コンテキスト | 対象期間 | 元の値(円) |
|---|---|---|
Prior1YearDuration |
2024-04-01〜2025-03-31 | 316,934,000,000 |
CurrentYearDuration |
2025-04-01〜2026-03-31 | 323,609,000,000 |
このサンプルの指定した2件には、scenarioとsegmentがありません。だからといって、他の書類でも「これらがなければ必ず同じ対象範囲」と判定してよいわけではありません。この実習は、確認済みの公式サンプルの2件に対象を限定しています。
次をcompare_edinet_sample.pyとして、前回のPythonファイルと同じフォルダーに保存します。前回の名前空間定義、ファイル名、候補が1件か確かめる関数を読み込んで使います。
"""Two annual facts from one official example; not a general parser."""
import csv
import hashlib
import json
import sys
from datetime import datetime, timezone
from decimal import Decimal
from pathlib import Path
from zipfile import ZipFile
from lxml import etree
from read_edinet_sample import NS, FILENAME, exactly_one
def compare(path):
with ZipFile(path) as archive:
member = exactly_one([n for n in archive.namelist()
if n.endswith('/PublicDoc/' + FILENAME)], 'sample file')
raw = archive.read(member)
root = etree.fromstring(raw, etree.XMLParser(resolve_entities=False, no_network=True))
rows = []
for context_id, dates in [
('Prior1YearDuration', ('2024-04-01', '2025-03-31')),
('CurrentYearDuration', ('2025-04-01', '2026-03-31')),
]:
fact = exactly_one(root.xpath('./p:NetSales[@contextRef=$c]', namespaces=NS, c=context_id), 'fact')
context = exactly_one(root.xpath('./x:context[@id=$c]', namespaces=NS, c=context_id), 'context')
unit = exactly_one(root.xpath('./x:unit[@id=$u]', namespaces=NS, u=fact.get('unitRef')), 'unit')
if fact.get('{'+NS['xsi']+'}nil') in {'true', '1'}:
raise ValueError('nil is not zero')
if context.find('x:scenario', NS) is not None or context.find('x:entity/x:segment', NS) is not None:
raise ValueError('Unexpected dimensions; inspect the context')
entity = context.findtext('x:entity/x:identifier', namespaces=NS)
start = context.findtext('x:period/x:startDate', namespaces=NS)
end = context.findtext('x:period/x:endDate', namespaces=NS)
if entity != 'X99001-000' or (start, end) != dates:
raise ValueError('Unexpected entity or period')
if len(unit) != 1 or unit[0].tag != '{'+NS['x']+'}measure':
raise ValueError('Unexpected unit structure')
prefix, currency = (unit[0].text or '').strip().split(':', 1)
if unit[0].nsmap.get(prefix) != 'http://www.xbrl.org/2003/iso4217' or currency != 'JPY':
raise ValueError('Expected JPY')
value = Decimal((fact.text or '').strip())
rows.append(dict(entity=entity, namespace=NS['p'], local_name='NetSales',
context_id=context_id, start=start, end=end, unit=currency,
decimals=fact.get('decimals'), value=str(value),
million_yen=str(value / Decimal('1000000'))))
return dict(source_type='FSA official sample, not a real company', member=member,
sha256=hashlib.sha256(raw).hexdigest(),
extracted_at=datetime.now(timezone.utc).isoformat(), rows=rows)
if __name__ == '__main__':
result = compare(sys.argv[1])
Path('edinet-comparison.json').write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding='utf-8')
with open('edinet-comparison.csv', 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=result['rows'][0].keys())
writer.writeheader()
writer.writerows(result['rows'])
print(json.dumps(result, ensure_ascii=False, indent=2))
実行するときは、公式サンプルZIPの場所を引数に渡します。
python compare_edinet_sample.py edinet-official-sample.zip
実行したフォルダーにedinet-comparison.jsonとedinet-comparison.csvができます。CSVは表計算ソフトで開いて値を眺めるため、JSONは出典情報と値を一緒に残すために使います。この簡単なCSV単体には書類のハッシュなどを入れていないので、JSONと組にして保存してください。
| 保存する表示値(百万円) | 前期比較値 | 当期値 |
|---|---|---|
| 売上高 | 316,934 | 323,609 |
コードは、2つのコンテキストについて順に次を確認しています。
- 売上高の候補、コンテキスト、単位が、それぞれ1件に決まること。
-
nilではないこと。対象企業・期間が、確認済みのサンプルと一致すること。 - 想定外の次元情報がないこと。単位が単純な円単位であること。
- 元の値を
Decimalで読み、表示用に100万で割ること。
decimals="-6"を理由に、元の値へ100万を掛ける処理はありません。精度と表示倍率の違いはEDINET③で確認したとおりです。
extracted_atはコードを実行した日時です。EDINETの提出日時や、値が初めて世の中に公表された日時ではありません。また、このZIPは公式の説明用サンプルなので、実在する提出書類のdocIDを推測して付けていません。ファイル名の日付からも作りません。
元ZIPの日本語パスが環境によって読みづらく見える場合は、前回と同じく、コードは末尾のPublicDoc/とファイル名で対象を選びます。出力のパス文字列はZIPを読み取った結果であり、それを正しい日本語に復元したものではありません。
エラーになったときは、対象の違いを先に確かめる
Unexpected ...が出た場合は、想定していない単位・期間・次元などを検出したという意味です。チェックを消して無理に動かす前に、ファイル名、名前空間、コンテキストを見直してください。
汎用の解析処理では、異なる会計基準、提出者独自の項目、次元、重複するfact、訂正の扱いなどへ広げる必要があります。このコードはそこまで対応するものではありません。「知らない条件を黙って採用せず、止まって確かめる」練習と考えてください。
7. 保存した値を、比較条件が分かるグラフにする
今回の2件を百万円へそろえると、次のグラフになります。元の値を保存したまま、表示時に単位を変えています。
出典:金融庁「2026年版EDINETタクソノミの公表」のサンプルインスタンス。指定ファイルのNetSales2件を抽出し、図表化しました。実在企業の業績ではありません。
計算すると、差額は323,609 − 316,934 = 6,675百万円です。前期比較値を分母にした増減率は、6,675 ÷ 316,934 × 100 ≒ 2.1%になります。この説明を図にも付けると、何と何を比較したのかが伝わります。
今回の棒グラフは、縦軸をゼロから始めています。差が小さいからといって下の部分を切り落とすと、増加幅を大きく見せてしまうことがあるためです。欠損がある年はゼロの棒で埋めず、「値を確認できない」と分かる表示にします。
実際の画面では、棒を選んだときに、期間・単位・出典書類へたどれると便利です。保存の段階で出典を持たせておけば、画面側でも「この数値はどこから来たのか」を示せます。
累計と四半期単独、実績と予想を混ぜないグラフの作り方は、TDnet解析④を参照してください。この回の2件は年次の比較であり、四半期単独値へ分解するものではありません。
8. 自分で解析する部分と、APIを使う部分を選ぶ
ここまでの作業を続けていくと、取得・保管、定義の管理、値の抽出、比較条件の確認、更新への対応、画面表示という複数の工程が見えてきます。すべてを一度に実装する必要はありません。自分が作りたい画面に必要な部分から選ぶと進めやすくなります。
私たち株式会社Crabgrassでは、資産管理・財務分析サービスを作るためのデータAPI、ZENITH Codeを開発しています。
サービス内では、次の順で読むと、この記事の話をAPI選びにつなげられます。
- EDINET・XBRLを読むためのガイド:書類・タグ・コンテキストの位置関係を整理する。
- 財務データAPIの選び方:作りたい画面に応じて、どの種類のデータを使うか考える。
- 財務ファクトのドキュメント:項目単位のデータを利用したいときの仕様を確認する。
- 財務諸表フルのドキュメント:財務諸表の構造を利用する場合の仕様を確認する。
APIを使っても、対象期間・単位・訂正や履歴の提供範囲を理解する必要はあります。この記事の保存モデルや公式サンプルは、ZENITH Codeのレスポンスそのものではありません。利用時には、実際のエンドポイントの仕様と取得結果を確かめてください。
参考資料
- 金融庁:2026年版EDINETタクソノミの公表 — サンプルインスタンス、提出者別タクソノミ作成ガイドライン、報告書インスタンス作成ガイドライン。
- EDINET:操作ガイド等 — API仕様書などの案内。
- EDINET API仕様書 Version 2 — 書類一覧の識別子・状態・書類種別コード。
この回で保存したのは、比較可能と確認した2件と、その出典です。値が増えるほど、値そのものだけでなく、採用した条件と出典へ戻れることが役に立ちます。



