EDINETから有価証券報告書をXBRLファイル形式で取得し、テキストデータを抽出する(API version2対応)
EDINETは、金融庁が公開している有価証券報告書などの電子開示閲覧サイトです。
有価証券報告書をPDF形式でダウンロードしたり、XBRL形式でデータとして直接取得することも可能です。
この記事ではEDINETのAPI(version2対応)とPythonを使って、
1. 有価証券報告書をXBRL形式で取得する方法
2. XBRLファイルからテキストデータを抽出する方法
の2点をご紹介していきます。
事前準備
API_KEYの設定
GoogleコラボラトリーのシークレットにEDINETのAPI-KEYを保存しておきます。
具体的な保存の方法は以下を参照してください。
# EDINETAPIのトークンを取得する
from google.colab import userdata
edinet_api_key = userdata.get('EDINET_API_KEY')
docID(書類管理番号)の取得
EDINETでは一つ一つの有価証券報告書をdocID(書類管理番号)で管理しています。
以下のリンクを参照して、有価証券報告書のdocIDをあらかじめ取得し、edinet_dfに格納します。
有価証券報告書を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