🐕

EDINETから有価証券報告書をXBRLファイル形式で取得し、テキストデータを抽出する(API version2対応)

2024/05/06に公開

EDINETは、金融庁が公開している有価証券報告書などの電子開示閲覧サイトです。
有価証券報告書をPDF形式でダウンロードしたり、XBRL形式でデータとして直接取得することも可能です。

この記事ではEDINETのAPI(version2対応)とPythonを使って、
1. 有価証券報告書をXBRL形式で取得する方法
2. XBRLファイルからテキストデータを抽出する方法
の2点をご紹介していきます。

事前準備

API_KEYの設定

GoogleコラボラトリーのシークレットにEDINETのAPI-KEYを保存しておきます。
具体的な保存の方法は以下を参照してください。
https://zenn.dev/robes/articles/04cc622f31c61b

# EDINETAPIのトークンを取得する
from google.colab import userdata

edinet_api_key = userdata.get('EDINET_API_KEY')

 
 

docID(書類管理番号)の取得

EDINETでは一つ一つの有価証券報告書をdocID(書類管理番号)で管理しています。
以下のリンクを参照して、有価証券報告書のdocIDをあらかじめ取得し、edinet_dfに格納します。
https://zenn.dev/robes/articles/f6dfcc5cfbbdb6

 
 

有価証券報告書をXBRL形式で取得する

ライブラリーのインポート

from bs4 import BeautifulSoup
import zipfile
import io
import requests
import pandas as pd
from tqdm.notebook import tqdm
import time
import glob
import re

クラスの定義

GetReportFromEdinetクラスを定義します。
time.sleep()で適切な秒数を設定ましょう。
取得したファイルは、Googleコラボラトリーのcontentディレクトリ以下に格納していきます

class GetReportFromEdinet:

    def __init__(self,docid_list):
        self.docid_list = docid_list

    def get_xbrl_file(self):
        for docid in tqdm(self.docid_list):
            # 書類取得APIのエンドポイント
            url = "https://api.edinet-fsa.go.jp/api/v2/documents/" + docid
            print(url)
            time.sleep(5)


            #書類取得APIのパラメータ
            params ={"type":1,"Subscription-Key":edinet_api_key}
            res = requests.get(url,params=params, verify=False)

            #ファイルへの出力
            print(res.status_code)
            if res.status_code == 200:
            # レスポンスからZIPファイルを読み込む
                with zipfile.ZipFile(io.BytesIO(res.content)) as z:
                # ZIPファイル内のすべてのファイルをループ処理
                    for file in z.namelist():
                        if file.startswith("XBRL/PublicDoc/") and file.endswith(".xbrl"):
                        # .xbrlファイルを見つけたら、それをディスクに書き込む
                            z.extract(file, path=f'/content/{docid}/')

クラスの実行

クラスのインスタンス化

grfe = GetReportFromEdinet(docid_list)

 
クラスメソッドの実行

%%time
grfe.get_xbrl_file()

contentディレクトリ直下に、docIDディレウトリが生成され、そのディレクトを潜っていくと、XBRLファイルを確認できます。

テキストデータを抽出する

ここでは、テキストデータとして以下の非財務情報を抽出していきます。

  • 経営方針、経営環境及び対処すべき課題等
  • 事業等のリスク
  • 従業員の状況
  • サステナビリティに関する考え方及び取組

xbrlパーサのインストールとインポート

#xbrlパーサのインストール
!pip install edinet_xbrl
#xbrlパーサのインポート
from edinet_xbrl.edinet_xbrl_parser import EdinetXbrlParser

xbrlパーサをインストールしておきましょう

parser = EdinetXbrlParser()

クラスの定義

class GetTextFromXBRL:

    def __init__(self, keys, context_ref, docid_list):
        self.keys = keys
        self.context_ref = context_ref
        self.docid_list = docid_list

    def parse_xbrl(self, xbrl_path):
        try:
            return parser.parse_file(xbrl_path), None
        except Exception as e:
            return None, str(e)

    def extract_data_from_xbrl(self, edinet_xbrl_object, key):
        if not edinet_xbrl_object:
            return None
        data = edinet_xbrl_object.get_data_by_context_ref(key, self.context_ref)
        if data:
            value = data.get_value()
            if value:
                return BeautifulSoup(value, 'html.parser').get_text(strip=True)
        return None
    
     #EDINETコードの取得
    def extract_dynamic_code(self,xbrl_path):
        pattern = r'jpcrp030000-asr-001_E(\d{5})'
        match = re.search(pattern, xbrl_path)
        if match:
            return "E" + match.group(1)
        else:
            return None

    def get_text_data_generic(self, include_sustaina=True):
        columns = ["docID"] + list(self.keys.keys())
        if include_sustaina:
            columns.append("サステナビリティ方針")
        extracted_data_list = []
        
        for _docid in tqdm(self.docid_list):
            xbrl_path = f'/content/{_docid}/XBRL/PublicDoc/*.xbrl'
            xbrl_paths = glob.glob(xbrl_path)
            if xbrl_paths:
                xbrl_path = xbrl_paths[0]
                edinet_xbrl_object, error = self.parse_xbrl(xbrl_path)
                if error:
                    print(f"Error parsing XBRL for {_docid}: {error}")
                    continue

                row_data = [_docid]
                for key_text in self.keys.values():
                    extracted_text = self.extract_data_from_xbrl(edinet_xbrl_object, key_text)
                    row_data.append(extracted_text)
                
                if include_sustaina:
                    dynamic_code = self.extract_dynamic_code(xbrl_path)
                    key_text = f"jpcrp030000-asr_{dynamic_code}-000:DisclosureOfSustainabilityRelatedFinancialInformationTextBlock"
                    extracted_text = self.extract_data_from_xbrl(edinet_xbrl_object, key_text)
                    row_data.append(extracted_text)

                extracted_data_list.append(row_data)

        return pd.DataFrame(extracted_data_list, columns=columns)

クラスの実行

keys(辞書)・cotext_refの指定

keys = {
    "経営方針":"jpcrp_cor:BusinessPolicyBusinessEnvironmentIssuesToAddressEtcTextBlock", #経営方針、経営環境および対処すべき課題等
    "事業等のリスク":"jpcrp_cor:BusinessRisksTextBlock", #事業等のリスク
    "従業員の状況":"jpcrp_cor:InformationAboutEmployeesTextBlock", #従業員の状況
}

context_ref = "FilingDateInstant"

クラスのインスタンス化

gtfb = GetTextFromXBRL(keys,context_ref,docid_list)

クラスメソッドの実行
データフレームを取得します

%%time
text_df = gtfb.get_text_data_generic()

出力結果
このようなデータフレームが出力されれば成功です

Edinetデータとの統合

会社名、EDINETコード、業種、決算日などを格納したedinet_dfと統合します。

def merge_df(df1, df2,how = "left"):
    #最初の2つのDataFrameを結合
    merged_df = pd.merge(df1, df2, on='docID', how=how)

    return merged_df

merge_df(edinet_df,text_df)

Discussion