K_DM Book
用語集
対象者 標準
対象者別の入口 →

対象者の設定はこのブラウザの中にだけ保存されます。

言語 JA

EDINET②

05.4.3

EDINET②

最終更新 2024-07-15 読了時間 7 分
まとめ
前のページで EDINET の書類を PDF として取りましたが、売上高や EPS を数値として扱うには、同じ書類取得 API に type=5 を付けて返る CSV(XBRL を表形式に変換したもの)か、type=1 の XBRL 本体を使います。このページでは商船三井・日本郵船・玉井商船・川崎汽船・飯野海運の 5 社の四半期報告書の docID を辞書にし、ZIP をダウンロードして展開する関数 save_csv() で CSV を集め、要素ID(jpcrp_cor:BasicEarningsLossPerShareSummaryOfBusinessResults など)と コンテキストID(CurrentQuarterDuration など)で絞った値を seaborn.barplot で 5 社並べます。後半では edinet_xbrl ライブラリで XBRL を直接読み、Prior4YearDuration から CurrentYearDuration までの 5 年分の 1 株配当を棒グラフにします。読み終えると、EDINET の CSV の「要素 ID × コンテキスト ID で 1 つの値が決まる」構造が分かり、欲しい財務項目を名前で取り出せるようになります。

直感 #

四半期報告書の CSV は、損益計算書のような「表」ではなく、「どの項目(要素 ID)の、どの期間(コンテキスト ID)の値はいくら」という 3 つ組が 1 行ずつ縦に並んだ形で届きます。たとえば EPS なら要素 ID が jpcrp_cor:BasicEarningsLossPerShareSummaryOfBusinessResults、当四半期なら コンテキスト ID が CurrentQuarterDuration で、この 2 つを指定すると値が 1 つに決まります。5 社の CSV を縦に積んで 会社名 列を付けておけば、同じ 2 つの条件で絞るだけで 5 社分の EPS が並び、棒グラフにできます。XBRL ファイルを直接読む場合も考え方は同じで、get_data_by_context_ref(要素ID, コンテキストID) で 1 つの値を取ります。

以下のコンテンツでは EDINET閲覧サイト から取得できる情報をもとにデータを加工して作成しています。コードの実行や取得したデータの利用については EDINET の利用規約を確認し理解した上で行ってください。Version 2 の API はログイン認証と API キーの発行が必要で、キーは環境変数 EDINET_API_KEY に置いています。

使用時には必ず利用規約(PDF・web)を確認した上で常識の範囲内での使用にとどめてください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import os
import time
import zipfile

import pandas as pd
import requests

API_ENDPOINT = "https://disclosure.edinet-fsa.go.jp/api/v2"  # v2を使用する

def save_csv(docID, type=5):
    """EDINETからデータを取得してフォルダに保存する

    Args:
        docID (str): DocID
    """
    assert type in [1, 2, 3, 4, 5], "typeの指定が間違っている"
    if type == 1:
        print(f"{docID}のXBRLデータを取得中")
    elif type == 2:
        print(f"{docID}のpdfデータを取得中")
    elif type in {3, 4}:
        print(f"{docID}のデータを取得中")
    elif type == 5:
        print(f"{docID}のcsvデータを取得中")
        time.sleep(5)

    r = requests.get(
        f"{API_ENDPOINT}/documents/{docID}",
        {
            "type": type,
            "Subscription-Key": os.environ.get("EDINET_API_KEY"),
        },
    )

    if r is None:
        print("データの取得に失敗しました。csvFlag==1かどうか確認してください。")
    else:
        os.makedirs(f"{docID}", exist_ok=True)
        temp_zip = "uuid_89FD71B5_CD7B_4833-B30D‗5AA5006097E2.zip"

        with open(temp_zip, "wb") as f:
            for chunk in r.iter_content(chunk_size=1024):
                f.write(chunk)

        with zipfile.ZipFile(temp_zip) as z:
            z.extractall(f"{docID}")

        os.remove(temp_zip)

書類取得 API で 5 社の CSV を取る #

EDINET のページと同じ手順で調べた 5 社の四半期報告書の docID を辞書にし、save_csv で 1 社ずつ CSV の ZIP を取得して展開します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
docID_dict = {
    "商船三井": "S100STH6",
    "日本郵船": "S100SS7P",
    "玉井商船株式会社": "S100STLS",
    "川崎汽船": "S100SRTI",
    "飯野海運": "S100SP9O",
}

for docID in docID_dict.values():
    save_csv(docID, type=5)
S100STH6のcsvデータを取得中
S100SS7Pのcsvデータを取得中
S100STLSのcsvデータを取得中
S100SRTIのcsvデータを取得中
S100SP9Oのcsvデータを取得中

取得したデータの確認 #

展開先の XBRL_TO_CSV フォルダには複数の CSV が入っています。企業内容等の開示に関する jpcrp で始まるファイルを読み、会社名の列を足して 5 社分を縦に積みます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
dfs = []

for companyName, docID in docID_dict.items():
    csv_savedir = os.path.join(docID, "XBRL_TO_CSV")
    filelist = [f for f in os.listdir(csv_savedir) if f.startswith("jpcrp")]
    if len(filelist) > 0:
        df = pd.read_csv(
            os.path.join(csv_savedir, filelist[0]), encoding="utf-16", sep="\t"
        )
        df["会社名"] = [companyName for _ in range(df.shape[0])]
        dfs.append(df)

all_data = pd.concat(dfs)
all_data.head()

要素ID項目名コンテキストID相対年度連結・個別期間・時点ユニットID単位値会社名
0jpdei_cor:NumberOfSubmissionDEI提出回数、DEIFilingDateInstant提出日時点その他時点pureNaN1商船三井
1jpcrp_cor:NetSalesSummaryOfBusinessResults売上高、経営指標等Prior1YTDDuration前年度同四半期累計期間その他期間JPY円1257968000000商船三井
2jpcrp_cor:NetSalesSummaryOfBusinessResults売上高、経営指標等CurrentYTDDuration当四半期累計期間その他期間JPY円1218664000000商船三井
3jpcrp_cor:NetSalesSummaryOfBusinessResults売上高、経営指標等Prior1YearDuration前期その他期間JPY円1611984000000商船三井
4jpcrp_cor:OrdinaryIncomeLossSummaryOfBusinessR...経常利益又は経常損失(△)、経営指標等Prior1YTDDuration前年度同四半期累計期間その他期間JPY円739254000000商船三井

要素 ID で 1 項目を取り出してみる #

項目の名前(要素 ID)は EDINET タクソノミ の概要説明 に一覧があります。まず配当に関する注記 jpcrp_cor:NotesRegardingDividendTextBlock を抽出して、5 社の値がどう入っているかを見ます。

1
2
3
print(
    all_data.query(f"要素ID=='jpcrp_cor:NotesRegardingDividendTextBlock'")[["会社名", "値"]]
)
          会社名                                                  値
462      商船三井  1.配当金支払額(決議)株式の種類配当金の総額(百万円)1株当たり配当額(円)基準日効力発生...
463      商船三井  1.配当金支払額(決議)株式の種類配当金の総額(百万円)1株当たり配当額(円)基準日効力発生...
431      日本郵船  配当金支払額(決議)株式の種類配当金の総額(百万円)配当の原資1株当たり配当額(円)基準日効...
432      日本郵船  配当金支払額(決議)株式の種類配当金の総額(百万円)配当の原資1株当たり配当額(円)基準日効...
334  玉井商船株式会社  前第3四半期連結累計期間(自 2022年4月1日 至 2022年12月31日)1.配当金支払...
335  玉井商船株式会社  当第3四半期連結累計期間(自 2023年4月1日 至 2023年12月31日)1.配当金支払...
380      川崎汽船  1.配当金支払額(決議)株式の種類配当金の総額(百万円)1株当たり配当額(円)基準日効力発生...
382      川崎汽船  1.配当金支払額(決議)株式の種類配当金の総額(百万円)1株当たり配当額(円)基準日効力発生...
385      飯野海運  1 配当金支払額(決議)株式の種類配当金の総額(百万円)1株当たり配当額(円)基準日効力発生...
386      飯野海運  1 配当金支払額(決議)株式の種類配当金の総額(百万円)1株当たり配当額(円)基準日効力発生...

5 社の EPS と粗利益を棒グラフで比べる #

1 株当たり当期純利益(EPS)と粗利益を、要素 ID とコンテキスト ID の 2 条件で絞って 5 社分並べます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import matplotlib.pyplot as plt
import seaborn as sns

def compare_company_IR(data, contextId, elementId, elementJpName):
    plot_data = data.query(f"要素ID=='{elementId}' and コンテキストID=='{contextId}'").copy()
    plot_data[elementJpName] = pd.to_numeric(plot_data["値"])
    sns.barplot(data=plot_data, x="会社名", y=elementJpName)
    plt.ylabel(elementJpName)
    plt.show()

compare_company_IR(
    all_data,
    "CurrentQuarterDuration",
    "jpcrp_cor:BasicEarningsLossPerShareSummaryOfBusinessResults",
    "EPS",
)

compare_company_IR(all_data, "CurrentYTDDuration", "jppfs_cor:GrossProfit", "粗利益")

1株当たり当期純利益又は当期純損失(いわゆるEPS)の変化を可視化して比較したいと思いますの図

1株当たり当期純利益又は当期純損失(いわゆるEPS)の変化を可視化して比較したいと思いますの図

XBRL ファイルからデータを取得する #

CSV は四半期報告書に含まれる期間しか持ちませんが、XBRL 本体には「主要な経営指標等の推移」として過去 5 年分の値が入っています。pip install edinet-xbrl でライブラリを入れ、XBRL を直接読みます。参考文献:EDINETのXBRL用のPythonライブラリを作った - Parser編

値を取るときはコンテキスト ID で時期を指定します。Duration は期間(売上や利益など)、Instant は時点(総資産など)を表し、以下の __NUM__ には 0〜9 が入ります(ドキュメント)。

Prior__NUM__YearDuration
CurrentYearDuration
Prior__NUM__YearInstant
CurrentYearInstant
CurrentQuarterDuration
CurrentQuarterInstant
Prior__NUM__QuarterDuration
Prior__NUM__QuarterInstant
CurrentYTDDuration
Prior__NUM__YTDDuration
InterimDuration
InterimInstant
Prior__NUM__InterimDuration
Prior__NUM__InterimInstant
1
2
for docID in docID_dict.values():
    save_csv(docID, type=1)
S100STH6のXBRLデータを取得中
S100SS7PのXBRLデータを取得中
S100STLSのXBRLデータを取得中
S100SRTIのXBRLデータを取得中
S100SP9OのXBRLデータを取得中
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from edinet_xbrl.edinet_xbrl_parser import EdinetXbrlParser

parser = EdinetXbrlParser()
edinet_xbrl_object = parser.parse_file(
    "S100QZLI/XBRL/PublicDoc/jpcrp030000-asr-001_E04236-000_2023-03-31_01_2023-06-20.xbrl"
)

key = "jppfs_cor:Assets"
context_ref = "Prior1YearInstant"
current_year_assets = edinet_xbrl_object.get_data_by_context_ref(
    key, context_ref
).get_value()
current_year_assets
c:\Users\nanja-win-ms\miniconda3\envs\py39\lib\site-packages\bs4\builder\__init__.py:545: XMLParsedAsHTMLWarning: It looks like you're parsing an XML document using an HTML parser. If this really is an HTML document (maybe it's XHTML?), you can ignore or filter this warning. If it's XML, you should know that using an XML parser will be more reliable. To parse this document as XML, make sure you have the lxml package installed, and pass the keyword argument `features="xml"` into the BeautifulSoup constructor.
  warnings.warn(


'2686701000000'

配当の推移 #

1 株当たり配当額(DividendPaidPerShareSummaryOfBusinessResults)を、4 年前から当年までの 5 つのコンテキスト ID で取り出して棒グラフにします。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
key = "jpcrp_cor:DividendPaidPerShareSummaryOfBusinessResults"
edinet_xbrl_object = parser.parse_file(
    r"S100QZLI\XBRL\PublicDoc\jpcrp030000-asr-001_E04236-000_2023-03-31_01_2023-06-20.xbrl"
)
interimDividendPaidPerShares = []
context_refs = [
    "Prior4YearDuration_NonConsolidatedMember",
    "Prior3YearDuration_NonConsolidatedMember",
    "Prior2YearDuration_NonConsolidatedMember",
    "Prior1YearDuration_NonConsolidatedMember",
    "CurrentYearDuration_NonConsolidatedMember",
]

for context_ref in context_refs:
    current_year_assets = edinet_xbrl_object.get_data_by_context_ref(
        key, context_ref
    ).get_value()
    interimDividendPaidPerShares.append(pd.to_numeric(current_year_assets))

plt.bar(
    [i for i in range(len(interimDividendPaidPerShares))], interimDividendPaidPerShares
)
<BarContainer object of 5 artists>

配当の推移の図

実行結果の読み方 #

5 本の棒が左(4 年前)から右(当年)へ並び、海運市況が好転した 2021〜2022 年度に配当が大きく増えた様子が読めます。1 社の有価証券報告書 1 ファイルで 5 年分が揃うのは、XBRL に「主要な経営指標等の推移」の表が含まれているためで、CSV 版の四半期報告書では当期と前年同期の 2 期分しか取れません。

使いどころと落とし穴 #

CSV の 値 列は数値も文章もすべて文字列で、TextBlock で終わる要素は HTML を含む長文なので、数値として扱う前に pd.to_numeric が必要です。同じ要素 ID でもコンテキスト ID が違えば別の行です。query で両方を条件にしないと barplot が複数期の平均を描いてしまいます。配当のように単体で支払う項目は _NonConsolidatedMember 付きのコンテキストにしか値が無く、連結のコンテキストを指定すると None が返ります。csvFlag が 0 の古い書類には CSV が無いので、2020 年以前の書類を集めるなら XBRL(type=1)を使います。

まとめ #

EDINET の数値データは「要素 ID × コンテキスト ID → 値」の 3 つ組で届きます。CSV なら save_csv(docID, type=5) で取って query("要素ID==... and コンテキストID==...") で絞り、XBRL なら type=1 で取って get_data_by_context_ref(key, context_ref).get_value() で読む、という 2 通りの入口があり、どちらも同じ ID の組み合わせで値を指定します。5 社の EPS・粗利益の比較には会社名列を付けて concat した CSV が、1 社の 5 年分の配当推移には有価証券報告書の XBRL が向いています。