Claude Media
EDINET APIで有価証券報告書を同業他社と比較 — Claude Codeで回す手順

EDINET APIで有価証券報告書を同業他社と比較 — Claude Codeで回す手順

EDINET API v2の書類一覧と書類取得(CSV)を使い、複数社の有価証券報告書から指定項目を表にまとめるスクリプトをClaude Codeで作る手順。APIキーの扱いと検証つき。

同業3〜5社の有価証券報告書から売上高や営業利益を拾い、並べて比べる作業は、PDFを開いて数字を写す形だと半日かかります。EDINET APIを使えば、報告書の取得から数値の抽出までをスクリプトにでき、そのスクリプトはClaude Codeに書かせられます。

ここでは、書類一覧APIで対象の報告書を見つけ、書類取得APIでCSV形式を落とし、指定した項目を比較表にするまでを順に扱います。APIキーの管理と、数字が合っているかの確かめ方も入れました。

EDINET APIの基本的な取り方はEDINET APIで有価証券報告書をClaudeに読ませるにあります。ここでは、複数社を同じ物差しで並べる部分(日付をなめる設計、決算期と会計基準のずれ、元の書類に戻れる表)を中心に扱います。

比較に使うのは書類一覧と書類取得の2本

EDINET API v2のうち、比較に使うのは書類一覧(/api/v2/documents.json)と書類取得(/api/v2/documents/{書類管理番号})です。ホストは https://api.edinet-fsa.go.jp で、ブラウザ上のJavaScriptからは呼べないため、PythonやNode.jsのスクリプトにします。

比較で効いてくる制約は、書類一覧が「会社ごと」ではなく「提出日ごと」に引く作りであることです。会社を指定して履歴を引くAPIは、この2本の中にありません。

APIキーはClaude Codeに読ませない

どちらのAPIも Subscription-Key パラメータにAPIキーが要ります。キーは .env に置いてスクリプトから読み、permissions.deny に Read(./.env) を入れてClaude Codeの読み取りを拒否します。発行手順と設定の詳細は基本編の記事に、権限ルールの確認方法は/permissionsコマンドの記事にあります。

CLAUDE.mdに比較ルールを書いておく

CLAUDE.mdは、Claudeがセッション開始のたびに読む指示ファイルです。EDINETの扱いは取り違えやすい点が多いので、先に書いておくと、スクリプトを作り直すたびに同じ説明をしなくて済みます。

## EDINET比較スクリプトのルール
- APIキーは .env の EDINET_API_KEY から読む。コード・ログ・会話に出さない
- 書類一覧は date 単位。type=2 で取得し、docTypeCode が 120 のものだけ使う
- 書類取得は type=5(CSV)。Content-Type が application/octet-stream でなければ失敗扱い
- CSV はタブ区切り・UTF-16LE。連結と個別を混ぜない。要素ID が jppfs で始まらない行は、連結・個別の列で判定しない
- 比べる会社の periodEnd がそろっているか、表を出す前に確かめる
- 取得した ZIP と CSV は cache/ に保存し、同じ書類を二度取りに行かない
- リクエストの間は 1 秒以上あける。429 が返ったら待って再試行する
- 抽出した値は、必ず書類管理番号と要素IDを一緒に出力する

最後の1行が検証の足場になります。表の数字から元の書類と要素に戻れるので、後で食い違いを追えます。

書類一覧APIで対象の報告書を絞る

書類一覧APIの必須パラメータは date(ファイル日付、YYYY-MM-DD)と Subscription-Key です。type は省略でき、既定の 1 はメタデータだけを返します。提出書類の一覧が欲しいときは type=2 を指定します。

リクエストの形は、仕様書のサンプルでは次のとおりです。

curl "https://api.edinet-fsa.go.jp/api/v2/documents.json\
?date=2023-04-01&type=2&Subscription-Key=$EDINET_API_KEY"

返ってくる results の各要素のうち、比較で使うのは次の項目です。docID(書類管理番号)は書類取得APIに渡します。secCode(証券コード、5桁)で対象企業を絞り、filerName を表の行見出しにします。docTypeCode は 120 が有価証券報告書、csvFlag が 1 の書類だけがCSVで取れ、periodEnd は決算期のそろい具合の確認に使います。

期間(自・至)は、有価証券報告書では事業年度が入ります。同業を比べるときは、periodEnd を見て決算期がそろっているかを確かめます。3月決算の会社と12月決算の会社を同じ列に並べると、数字が同じ年度を指さなくなります。

date に指定できるのは、当日以前で、直近の財務局営業日の24時から10年を経過していない日付です。土日祝日も指定できます。

1社ずつ引けないので日付をなめる

会社を指定する手段がないため、探す期間の日付を1日ずつ引いて、secCode が対象の会社に一致する docTypeCode 120を拾う形になります。1日が1リクエストなので、半年分なら約180回です。

回数を減らすには、対象の会社の提出日を先に絞ります。各社のIRページで有価証券報告書の提出日を確かめて日付のリストを作り、その日だけを引く方法があります。取りこぼしを避けたいときは、1か月分などの範囲をなめて、取れた書類管理番号をファイルに保存しておきます。

訂正報告書(130)と取下げの扱いは、後の節で書きます。

書類取得APIでCSVを落とす

数値を比べるなら、書類取得APIの type=5(CSV)が扱いやすい形式です。ZIPの中の XBRL_TO_CSV フォルダに入っていて、書類一覧の csvFlag が 1 の書類でしか取れません。

CSVはカンマではなくタブ区切りで、文字コードはUTF-16LEです。Pythonで読むときは文字コードと区切り文字を指定しないと文字化けします。1行目の見出しのうち、比較で使うのは「要素ID」(比較のキー)、「相対年度」、「連結・個別」、「単位」、「値」です。

失敗したときもHTTPステータスは200で、本体がエラー情報のJSONになります。成否は Content-Type で見分けます(ZIPなら application/octet-stream、失敗時は application/json)。CLAUDE.mdに「Content-Typeで判定する」と書いたのはこのためです。キー無効の401と、リクエスト過多の429も、書類一覧側ではJSONで返ります。

Claude Codeにスクリプトを書かせる

ここまでの仕様を渡して、取得スクリプトを書かせます。プロンプトは、入力と出力と禁止事項を分けて書きます。

EDINET API v2 で、次の証券コードの有価証券報告書(docTypeCode=120)の
CSV を取得し、comparison.csv にまとめるスクリプトを書いてください。
- 対象: 証券コードのリストは targets.txt、探す期間は 2026-06-01 から 2026-06-30
- まず取得と保存だけ作る。抽出は別の関数に分ける
- cache/ に ZIP を保存し、再実行で取り直さない
- CLAUDE.md のルールに従う。実行前に、何回リクエストするかを出力する

「何回リクエストするかを先に出させる」のは、日付をなめる設計では回数が膨らみやすいためです。見積もりを確認してから実行すれば、429を踏みにくくなります。

取得部分の骨格は、仕様書の記載に沿って組むと次の形になります。

import io, json, os, time, urllib.parse, urllib.request, zipfile
 
BASE = "https://api.edinet-fsa.go.jp/api/v2"
KEY = os.environ["EDINET_API_KEY"]
 
def get(path, **params):
    params["Subscription-Key"] = KEY
    url = f"{BASE}/{path}?" + urllib.parse.urlencode(params)
    with urllib.request.urlopen(url, timeout=60) as r:
        return r.headers.get_content_type(), r.read()
 
def list_reports(day, sec_codes):
    _, body = get("documents.json", date=day, type=2)
    data = json.loads(body)
    status = data.get("StatusCode") or data.get("metadata", {}).get("status")
    if str(status) not in ("None", "200"):
        raise RuntimeError(f"{day}: {status}")
    return [r for r in data.get("results", [])
            if r["docTypeCode"] == "120" and r["csvFlag"] == "1"
            and r["secCode"] in sec_codes]
 
def fetch_csv_rows(doc_id):
    ctype, body = get(f"documents/{doc_id}", type=5)
    if ctype != "application/octet-stream":
        raise RuntimeError(f"{doc_id}: {body[:200]!r}")
    with zipfile.ZipFile(io.BytesIO(body)) as z:
        name = next(n for n in z.namelist() if n.endswith(".csv"))
        text = z.read(name).decode("utf-16-le").lstrip("\ufeff")
    rows = [l.split("\t") for l in text.splitlines()]
    head = [h.strip('"') for h in rows[0]]
    return [dict(zip(head, [c.strip('"') for c in r])) for r in rows[1:]]

リクエストの合間には time.sleep(1) を入れます。分割はタブ単位の単純な処理で、値の中に改行や引用符が入る行は崩れるおそれがあります。気になるときは、csv.reader に delimiter="\t" を指定して読み替えるようClaudeに頼みます。

比較する項目を、まず1社のCSVから選ぶ

比較表に載せる項目の要素IDは、API仕様書には書かれていません。要素そのものはタクソノミ資料(資料一覧に掲載)で定義されているので、資料で探すか、実データから洗い出して決めます。推測で書かせると、存在しないIDを使ったスクリプトがエラーを出さずに空欄を返します。

そこで、いきなり抽出に進まず、1社のCSVで項目を洗い出す手順を挟みます。

cache/ の1社分のCSVから、項目名に「売上高」「営業利益」「純資産」
のいずれかを含む行を、要素ID・項目名・コンテキストID・相対年度・
連結・個別・値で一覧にしてください。まだ抽出スクリプトは書かないでください。

出力を見て、使う要素IDと、当期を指す 相対年度 の値を人が決めます。このとき、会社ごとに要素IDの接頭辞(jppfs で始まるか)も見ます。理由は次の節で説明します。相対年度 の値の表記は仕様書の説明に載っていないので、出力された文字列をそのままフィルタ条件に使います。決まったら、次のように辞書にして抽出関数を作らせます。

WANTED = {
    "売上高": "<洗い出しで選んだ要素ID>",
    "営業利益": "<洗い出しで選んだ要素ID>",
}
FISCAL = "<当期を指す相対年度の値>"
 
def pick(rows):
    out = {}
    for label, eid in WANTED.items():
        hit = [r for r in rows
               if r["要素ID"] == eid and r["相対年度"] == FISCAL]
        if eid.startswith("jppfs"):  # 連結・個別の列が使えるのはこの接頭辞だけ
            hit = [r for r in hit if r["連結・個別"] == "連結"]
        out[label] = hit[0]["値"] if hit else ""
    return out

該当がない会社は空欄のまま表に残します。無理に別の要素で埋めると、会社ごとに指す数字が変わります。

記述情報は値が30,000文字で切れる

事業等のリスクや経営方針のような文章の項目も、CSVでは「値」の列に入ります。ただし、30,000文字を超えると、CSVにはそこまでしか出力されません。長い項目を比べるときは、type=1 で本文のZIPを取り直します。

文章の比較は、数字の比較よりもClaudeの出番です。項目名に「リスク」を含む行を会社ごとに抜き、各社が挙げる論点を並べさせます。このとき「書いていない」ことの判断は、根拠の行を示させます。

日本基準とIFRSの会社が混ざるとき

同業を並べると、日本基準の会社と国際会計基準(IFRS)の会社が混ざるのはよくあることです。ここで引っかかるのが「連結・個別」の列です。閲覧操作ガイドによると、この列に「連結」か「個別」が入るのは要素IDが jppfs で始まるときだけで、それ以外の接頭辞の要素は、連結の数字でも一律に「その他」と出力されます。

先ほどの pick() が jppfs のときだけ連結で絞っているのは、このためです。r["連結・個別"] == "連結" を全項目にかけると、jppfs 以外の要素を使う会社は必ず空欄になります。

資料一覧には「タクソノミ要素リスト」と「国際会計基準タクソノミ要素リスト」が別の資料として載っていて、基準によって要素の体系が分かれています。同じ「売上高」でも、会社ごとに別の要素IDを選ぶ場面がありえます。洗い出しの段階で、次の3点を会社ごとに表にしておくと安全です。

  • 使う要素IDと、その接頭辞
  • 連結の数字かどうかを判断した根拠(jppfs なら列の値、それ以外なら項目名やコンテキストID)
  • 日本基準とIFRSで定義が異なりうる項目(営業利益など)は、同じ列に並べてよいかの判断

決算期のずれと複数年の並べ方

3月決算と12月決算を同じ列に置くと、数字が同じ年度を指さなくなります。periodEnd が会社間でそろっていないときは、表の列見出しを「当期」ではなく periodEnd の日付にし、ずれていることを表の注記に書きます。決算期が違う会社は、提出日も違うので、日付をなめる範囲も会社ごとに別に持ちます。

複数年を並べるときは、年ごとに別の書類(別の書類管理番号)を取りに行く前提で組みます。キャッシュは書類管理番号をファイル名にして、会社×年度の表に periodEnd を添えます。前期の値が同じCSVに入っているかは、相対年度 列の出力を見て確かめます。入っていれば、取得回数を減らせます。

訂正報告書と元の報告書の突き合わせ

訂正報告書の docTypeCode は 130 で、元の報告書(120)とは別の書類として一覧に出ます。比較表の前に、同じ会社・同じ periodEnd で 130 が出ていないかを日付の範囲内で調べます。

出ていたときは、元の書類の値と訂正後の書類の値を別の行で並べて、どの項目が変わったかを確かめます。表に載せる値は訂正後を使い、注記に書類管理番号を添えます。取下げられた書類(withdrawalStatus が 2)は比較の対象から外します。

表の数字が合っているか確かめる

自動で作った表は、そのままでは使いません。次の3点を確かめます。

  1. 1社分の売上高を、EDINETの閲覧画面の書類と見比べる。ここが合えば、要素IDとフィルタ条件は合っている
  2. periodEnd が、比べる会社の間でそろっているか
  3. 単位が、全社で同じか。CSVの「単位」列を表の注記に出す

Claudeにやらせるなら、「抽出した各値について、書類管理番号と要素IDを添えて一覧にして」と頼みます。元の行に戻れる形にしておけば、食い違いは1行ずつ追えます。

SECの提出書類を同じ発想で読む方法は、デューデリジェンスの記事にあります。APIで定点観測する型は、Search Console APIの記事と共通です。

比較表を載せるときの出典表記

EDINETの掲載情報は、利用規約でPDL1.0(公共データ利用規約)に準拠した条件で使えるとされています。比較表を社内資料や記事に載せるときは、次の点が効きます。

  • 出典を書く。規約の記載例は「出典:EDINET閲覧(提出)サイト(当該ページのURL)、PDL1.0(規約原文ページのURL)」の形です
  • 集計や抜粋など加工したときは、出典とは別に、加工したことと加工した主体を書く(例:「EDINET閲覧(提出)サイト(当該ページのURL)をもとに○○株式会社作成」)。加工した情報を、国が作成した未加工のものであるかのように公表してはいけません
  • EDINETタクソノミはこの規約の適用外です
  • ウェブサイトからのスクレイピングによる機械的な取得は禁止で、機械的に取るにはAPIを使います。短時間の大量アクセスも禁止行為に挙がっているので、1秒以上の間隔と429への対処が必要です

規約は事前の通知なしに変更されることがあるので、公開前に原文で確かめてください。

まとめ

EDINET APIで同業比較を回すときの難所は、コードの量ではありません。書類一覧が日付単位であること、エラーでも200が返ること、連結・個別の列が jppfs 以外の要素では使えないことの3つです。比較項目の要素IDはAPI仕様書には無く、タクソノミ資料か実データの洗い出しで決めます。ここをCLAUDE.mdと洗い出しの手順に先に入れておけば、Claude Codeが書くスクリプトは、元の書類に戻れる表を出してくれます。

この記事を共有:XはてブLinkedIn