2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PythonでEDINET財務データを取得する方法

2
Posted at

PythonでEDINETの財務データを取りたい。検索すると、requestsでZIPをダウンロードしてzipfileで展開してlxmlでXBRLをパースする記事が出てくる。2023年の記事で50行。動く。

ただし1社分。

3,709社×15年分×3つの会計基準に対応しようとすると、50行が500行になり、2週間が溶ける。自分もそこを通った。この記事では、最初にその「つらい方」を見せてから、3行で同じ結果を得る方法を紹介する。

EDINETから直接取る方法(つらい方)

まずは正攻法。EDINET APIでトヨタの有報ZIPを取得して、XBRLから売上高を抜く。

import requests
import zipfile
import io
from lxml import etree

# Step 1: EDINET APIで書類一覧を取得
res = requests.get("https://api.edinet-fsa.go.jp/api/v2/documents.json", params={
    "date": "2025-06-25",
    "type": 2,
    "Subscription-Key": "YOUR_EDINET_KEY",
})
docs = res.json()["results"]

# Step 2: トヨタ(E02144)の有報を探す
toyota_doc = next(d for d in docs if d["edinetCode"] == "E02144" and d["docTypeCode"] == "120")

# Step 3: ZIPダウンロード
zip_res = requests.get(
    f"https://api.edinet-fsa.go.jp/api/v2/documents/{toyota_doc['docID']}",
    params={"type": 1, "Subscription-Key": "YOUR_EDINET_KEY"},
)

# Step 4: ZIPからXBRLファイルを探す
with zipfile.ZipFile(io.BytesIO(zip_res.content)) as zf:
    xbrl_files = [f for f in zf.namelist() if f.endswith(".xbrl") and "AuditDoc" not in f]
    xbrl_content = zf.read(xbrl_files[0])

# Step 5: XBRLパース — ここからが地獄
tree = etree.fromstring(xbrl_content)
nsmap = {
    "jppfs": "http://disclosure.edinet-fsa.go.jp/taxonomy/jppfs/2023-12-01/jppfs_cor",
    "ifrs": "http://xbrl.ifrs.org/taxonomy/2023-03-23/ifrs-full",
}

# トヨタはIFRS。JP-GAAPなら別のnamespace。US-GAAPならさらに別。
revenue = tree.find(".//ifrs:Revenue", nsmap)
if revenue is None:
    revenue = tree.find(".//jppfs:NetSales", nsmap)  # JP-GAAPフォールバック

print(f"売上高: ¥{int(revenue.text):,}")

35行。動く(はず)。問題点:

  • 会計基準の分岐: トヨタはIFRSだからifrs:Revenue。任天堂はJP-GAAPだからjppfs:NetSales。ソフトバンクGはIFRSだがRevenueじゃなくOperatingRevenue。全部ハードコーディング。
  • 名前空間のバージョン: タクソノミは毎年更新される。2023-12-01がいつ2024-12-01に変わるか。
  • 単位: 円で出す会社、千円で出す会社、百万円で出す会社。XBRLのunitRefdecimals属性を見ないと金額が100万倍ずれる。
  • 期間の判定: contextRefで当期・前期を判別する必要がある。間違えると前年のデータを取ってくる。

1社ならなんとかなる。3,709社にスケールさせるのは別の話。

3行で同じ結果を得る方法

AxioraというAPIがある。EDINETの有報XBRLをパース・正規化して、REST APIで返してくれる。

pip install axiora
from axiora import Axiora

client = Axiora()  # 環境変数 AXIORA_API_KEY から読み取り
toyota = client.companies.retrieve_financials("7203", years=1)
print(f"売上高: ¥{toyota.data[0].revenue:,}")
# 売上高: ¥48,036,704,000,000

3行。ZIP展開もXBRLパースもnamespace分岐もない。証券コード7203(東証のティッカーそのまま)を渡すだけ。

SDKはStainlessで生成されていて(OpenAI、AnthropicのSDKと同じツール)、型付きPydanticモデルが返る。toyota.data[0].revenueがIDEで補完される。

無料プラン。クレカ不要。1日10,000リクエスト。

会計基準の違いが消える

EDINETの最大の罠は、日本の上場企業が3つの会計基準を使っていること。

  • トヨタ → IFRS(XBRLタグ: ifrs-full:Revenue
  • キーエンス → JP-GAAP(XBRLタグ: jppfs_cor:NetSales
  • 任天堂 → JP-GAAP(XBRLタグ: jppfs_cor:NetSales

自前パーサーだと3パターンの分岐が必要。APIだと:

from axiora import Axiora

client = Axiora()

# IFRS企業
toyota = client.companies.retrieve_financials("7203", years=1)
print(f"トヨタ(IFRS):   売上高 ¥{toyota.data[0].revenue:,}")
print(f"  会計基準: {toyota.data[0].accounting_standard}")

# JP-GAAP企業
keyence = client.companies.retrieve_financials("6861", years=1)
print(f"キーエンス(JP-GAAP): 売上高 ¥{keyence.data[0].revenue:,}")
print(f"  会計基準: {keyence.data[0].accounting_standard}")

nintendo = client.companies.retrieve_financials("7974", years=1)
print(f"任天堂(JP-GAAP): 売上高 ¥{nintendo.data[0].revenue:,}")
print(f"  会計基準: {nintendo.data[0].accounting_standard}")
トヨタ(IFRS):   売上高 ¥48,036,704,000,000
  会計基準: IFRS
キーエンス(JP-GAAP): 売上高 ¥1,059,145,000,000
  会計基準: JP-GAAP
任天堂(JP-GAAP): 売上高 ¥1,164,922,000,000
  会計基準: JP-GAAP

同じ.revenueフィールド。会計基準が違っても同じ変数名。裏側で52フィールド×3会計基準のXBRLタグマッピングが動いているが、使う側はif文を書かなくていい。

accounting_standardフィールドで、どの基準で報告されたかは確認できる。

トヨタの5年分を取得

financials = client.companies.retrieve_financials("7203", years=5)

for f in financials.data:
    rev = f.revenue / 1_000_000_000_000
    ni = f.net_income / 1_000_000_000_000
    print(f"  {f.fiscal_year}  売上: ¥{rev:.1f}兆  純利益: ¥{ni:.2f}兆  ROE: {f.roe:.1f}%")
  2025  売上: ¥48.0兆  純利益: ¥4.79兆  ROE: 13.0%
  2024  売上: ¥45.1兆  純利益: ¥5.07兆  ROE: 14.4%
  2023  売上: ¥37.2兆  純利益: ¥2.49兆  ROE: 8.5%
  2022  売上: ¥31.4兆  純利益: ¥2.87兆  ROE: 10.6%
  2021  売上: ¥27.2兆  純利益: ¥2.28兆  ROE: 9.4%

ROEまで計算済みで返ってくる。営業利益率、純利益率、自己資本比率なども同じ。52フィールド。

売上高ランキング:Top 10

pandasで表にする:

import pandas as pd
from axiora import Axiora

client = Axiora()
ranking = client.rankings.retrieve("revenue", limit=10)

rows = []
for r in ranking.data:
    rows.append({
        "順位": r.rank,
        "企業名": r.name_jp,
        "証券コード": r.securities_code,
        "セクター": r.sector,
        "売上高(兆円)": round(r.revenue / 1e12, 1),
    })

df = pd.DataFrame(rows)
print(df.to_string(index=False))
 順位               企業名 証券コード       セクター  売上高(兆円)
    1     トヨタ自動車株式会社       7203     輸送用機器          48.0
    2     本田技研工業株式会社       7267     輸送用機器          21.7
    3       三菱商事株式会社       8058       卸売業          18.6
    4     伊藤忠商事株式会社       8001       卸売業          14.7
    5       三井物産株式会社       8031       卸売業          14.7
    6   日本電信電話株式会社       9432  情報・通信業          13.7
    7   三菱UFJフィナンシャル・グループ  8306       銀行業          13.6
    8   ソニーグループ株式会社       6758     電気機器          13.0
    9     日産自動車株式会社       7201     輸送用機器          12.6
   10 ENEOSホールディングス株式会社  5020  石油・石炭製品         12.3

1回のAPIコール。11,245社のデータベースから最新の有報データでランキング。

スクリーニング:ROE15%以上の大企業

「ROEが15%以上で売上1,000億円以上」— クオンツが最初に回すフィルタ:

screen = client.screen.retrieve(
    roe_min=15.0,
    revenue_min=100_000_000_000,
    sort="roe_desc",
    limit=10,
)

for r in screen.data:
    name = r.company.name_jp
    print(f"  {name[:20]:<20}  ROE: {r.metrics.roe:.1f}%  売上: ¥{r.financials.revenue/1e9:,.0f}")

フィルタ条件:roe_min, roe_max, revenue_min, revenue_max, net_income_min, roa_min, sectorなど。全部サーバー側で処理される。

チャート:10年の売上推移

pip install axiora matplotlib
from axiora import Axiora
import matplotlib.pyplot as plt

client = Axiora()

companies = {"7203": "トヨタ", "6758": "ソニー", "6861": "キーエンス"}

fig, ax = plt.subplots(figsize=(10, 6))
for code, label in companies.items():
    financials = client.companies.retrieve_financials(code, years=10)
    years = [f.fiscal_year for f in reversed(financials.data)]
    rev = [f.revenue / 1e12 for f in reversed(financials.data)]
    ax.plot(years, rev, marker="o", label=label, linewidth=2)

ax.set_ylabel("売上高(兆円)")
ax.set_title("トヨタ vs ソニー vs キーエンス — 10年売上推移")
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("japan_revenue.png", dpi=150)
print("japan_revenue.png に保存しました")

japan_revenue.png

トヨタは圧倒的なスケール。でも傾きを見るとキーエンスが面白い。売上1兆円のセンサー企業が10年間ほぼ毎年2桁成長。しかも営業利益率51.9%。

非同期対応

複数社を並行で取りたい場合:

import asyncio
from axiora import AsyncAxiora

async def main():
    async with AsyncAxiora() as client:
        codes = ["7203", "6758", "6861", "9984", "6501"]
        tasks = [client.companies.retrieve_financials(code, years=1) for code in codes]
        results = await asyncio.gather(*tasks)

        for result in results:
            f = result.data[0]
            print(f"  {f.edinet_code}  売上: ¥{f.revenue/1e12:.1f}")

asyncio.run(main())

同じ型付きレスポンス。awaitするだけ。

コピペ用:完全スクリプト

ここまでの内容を1ファイルにまとめた。AXIORA_API_KEY環境変数をセットして実行するだけ。

#!/usr/bin/env python3
"""PythonでEDINET財務データを取得する — 完全スクリプト"""

import pandas as pd
from axiora import Axiora

client = Axiora()  # 環境変数 AXIORA_API_KEY から読み取り

# === 1. トヨタの5年分 ===
print("=== トヨタ (7203) — 5年分 ===")
for f in client.companies.retrieve_financials("7203", years=5).data:
    print(f"  {f.fiscal_year}  売上: ¥{f.revenue/1e12:.1f}兆  純利益: ¥{f.net_income/1e12:.2f}兆  ROE: {f.roe:.1f}%")

# === 2. 会計基準の違いを確認 ===
print("\n=== 会計基準の正規化 ===")
for code, name in [("7203", "トヨタ"), ("6861", "キーエンス"), ("7974", "任天堂")]:
    f = client.companies.retrieve_financials(code, years=1).data[0]
    print(f"  {name}{f.accounting_standard}): 売上 ¥{f.revenue/1e12:.2f}")

# === 3. 売上高ランキング Top 10 ===
print("\n=== 売上高ランキング Top 10 ===")
ranking = client.rankings.retrieve("revenue", limit=10)
rows = []
for r in ranking.data:
    rows.append({
        "順位": r.rank,
        "企業名": r.name_jp,
        "売上高(兆円)": round(r.revenue / 1e12, 1),
    })
print(pd.DataFrame(rows).to_string(index=False))

# === 4. 高ROEスクリーニング ===
print("\n=== ROE > 15%、売上 > 1,000億円 ===")
for r in client.screen.retrieve(
    roe_min=15, revenue_min=100_000_000_000, sort="roe_desc", limit=10
).data:
    print(f"  {r.company.name_jp[:20]:<20}  ROE: {r.metrics.roe:.1f}%")

print("\n全データ: 監査済みEDINET有報、XBRLパース済み。")
print("無料プラン: https://axiora.dev")
pip install axiora pandas
export AXIORA_API_KEY="your-key"
python edinet_quickstart.py

まとめ

EDINETは日本の財務データの正式なソース。ただし生のXBRLは、1社取るだけなら50行、3,709社に対応するなら数千行のパーサーと2週間の工期が必要になる。

axiora SDKを使えば、3行で同じデータが取れる。会計基準(JP-GAAP/IFRS/US-GAAP)の違いは正規化済み。ランキング、スクリーニング、成長率、ROEなどの指標も計算済みで返ってくる。

無料プランで全11,245社・全エンドポイントにアクセスできる。クレジットカード不要。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?