🔥

【自然言語処理】【Python】有価証券報告書の非財務情報(テキストデータ)を取得する

2022/09/02に公開
5

0. はじめに

上場企業が作成する有価証券報告書には、企業の経営成績がどうなっているのか?といった財務情報に加え、企業として何を課題と捉えどう取り組んでいくのか?といった非財務情報の記載があります。
最近は、気候変動リスクや人権問題への対応など企業の社会的責任の遂行に注目が集まる中、機関投資家が企業を評価する目線も、短期的な視点である財務情報から、中長期的な視点である非財務情報にうつりつつあります。
 
これからの時代、財務情報(数値データ)ではなく、非財務情報(テキストデータ)の分析ニーズが高まることが予想されます。こうした点も踏まえ、まことに僭越ながら、Pythonを使って有価証券報告書の非財務情報を取得する方法について、記載したいと思います。
 
具体的には、上場企業約2,500社分の有価証券報告書の 【経営方針、経営環境および対処すべき課題等】と【事業等のリスク】 を取得していきたいと思います。
 

 
 

1. 有価証券報告書のdocIDを取得する

  • EDINETに公開されている書類のひとつひとつは、docIDで管理されています。
  • まずは集めたい有価証券報告書のdocIDを取得する必要があります。
  • docIDの取得方法については、以下をご覧ください。

https://zenn.dev/robes/articles/bb7ea6202af81c

 
 

2. 有価証券報告書が入ったzipファイルを取得する

  • docIDの用意ができたところで、次は、EDINETのAPIを使って有価証券報告書が入ったzipファイルを取得していきます。
     

まずは、docIDが収納されたファイルを呼び出しましょう。

  • こちらの事例では、2022年3月期決算の約2,500社分を取得していきます。
import pandas as pd

codelist_df = pd.read_csv("/content/edinetcodelist.csv",index_col=0)
codelist_df


 

docIDをリスト化します

docid_list = codelist_df["docID"].tolist()
docid_list

 

有価証券報告書が入ったzipファイルを取得する

  • さきほど作ったdocid_listをfor文に入れて、EDINETのAPIエンドポイントから対象docIDのzipファイルを取得していきます。
  • 書類取得APIのリクエストパラメータをtype:1に指定します。
import requests
import zipfile
from tqdm import tqdm_notebook as tqdm
filename_list = []

for docid in tqdm(docid_list):
    # 書類取得APIのエンドポイント
    url = "https://disclosure.edinet-fsa.go.jp/api/v1/documents/" + docid
    print(url)
    
    # 書類取得APIのリクエストパラメータ
    params = {
        "type" : 1}
        
    # 出力ファイル名
    filename_list.append(docid + ".zip")

    # 書類取得APIの呼び出し
    res = requests.get(url, params=params, verify=False)
    # ファイルへ出力
    print(res.status_code)
    if res.status_code == 200:
        with open(docid+".zip", 'wb') as f:
            for chunk in res.iter_content(chunk_size=1024):
                f.write(chunk)

GoogleColaboratoryのcontentディレクトリ以下に、以下のような感じで、約2,500社分のzipファイルがずらっと並びます。

 
 

3.zipファイルを解凍する

for i, docid in enumerate(docid_list):
    print(filename_list[i])
    with zipfile.ZipFile(filename_list[i]) as zip_f:
      zip_f.extractall(docid)

 
 

4.XBRLファイルからテキストデータを取得する

  • ここからが、本番です。順番にやっていきましょう。
     

XBRLパーサのインストール

  • まずは、XBRLを読み取るためのライブラリーをインストールします
!pip install edinet-xbrl

 

必要なライブラリーのインポート

XBRLパーサのインスタンス化もしておきます

#ライブラリーのインポート
from edinet_xbrl.edinet_xbrl_parser import EdinetXbrlParser
import glob
from bs4 import BeautifulSoup
import pandas as pd

#インスタンス化
parser = EdinetXbrlParser()

 

空のデータフレームの用意

  • テキストデータを格納するための空のデータフレームをあらかじめ用意しておきます。
df = pd.DataFrame(columns=['docid','経営方針','事業等のリスク'])

 

テキストデータの取得

#XBRLファイルからテキストデータを取得し、データフレームにセットする
def extract_data_from_xbrl(edinet_xbrl_object, key, context_ref):
    """XBRLオブジェクトから指定されたキーとコンテキストを使用してデータを抽出し、
    BeautfulSoupでテキストを取得します。"""
    data = edinet_xbrl_object.get_data_by_context_ref(key, context_ref)
    if data:
        value = data.get_value()
        if value:
            return BeautifulSoup(value, 'html.parser').get_text(strip=True)
    return None

df_columns = df.columns  # 事前にdf.columnsを取得

keys = [
    #経営方針、経営環境および対処すべき課題等
    "jpcrp_cor:BusinessPolicyBusinessEnvironmentIssuesToAddressEtcTextBlock", 
    #事業等のリスク
    "jpcrp_cor:BusinessRisksTextBlock", 
]

context_ref = "FilingDateInstant"

for _docid in tqdm(docid_list):
    #xbrlファイルのパスを指定
    xbrl_path = f'/content/{_docid}/XBRL/PublicDoc/*.xbrl'
    xbrl_path = glob.glob(xbrl_path)[0]
    edinet_xbrl_object = parser.parse_file(xbrl_path)

    extracted_data = [_docid] + [extract_data_from_xbrl(edinet_xbrl_object, key, context_ref) for key in keys]
    tmp_se = pd.DataFrame([extracted_data], columns=df_columns)
    df = df.append(tmp_se)
for _docid in tqdm(docid_list):
    #xbrlファイルのパスを指定
    xbrl_path = '/content/' + _docid + '/XBRL/PublicDoc/*.xbrl'
    print(i)
    xbrl_path = glob.glob(xbrl_path)[0]
    print(xbrl_path)
    edinet_xbrl_object = parser.parse_file(xbrl_path)
    
    #keyとcontext_refを指定。keyが経営方針と事業等のリスクでは異なる
    #経営方針、経営環境および対処すべき課題等
    key_houshin= 'jpcrp_cor:BusinessPolicyBusinessEnvironmentIssuesToAddressEtcTextBlock'
    
    #事業等のリスク
    key_risk ='jpcrp_cor:BusinessRisksTextBlock'

    #context_refは経営方針と事業等のリスクともに共通
    context_ref='FilingDateInstant'
    
      #例外処理
    try:
    
        data_houshin = edinet_xbrl_object.get_data_by_context_ref(key_houshin, context_ref).get_value()
        soup_houshin = BeautifulSoup(data_houshin,'html.parser').get_text(strip=True)
        
        data_risk = edinet_xbrl_object.get_data_by_context_ref(key_risk, context_ref).get_value()
        soup_risk = BeautifulSoup(data_risk,'html.parser').get_text(strip=True)
        


        tmp_se = pd.DataFrame([[_docid], [soup_houshin],[soup_risk]], index=df.columns).T
        df = df.append(tmp_se)
    
    except AttributeError as e:
        print(e)

dfを表示すると、ちゃんととれています。


 
 

5.さいごに

いかがでしたでしょうか。
ここで作成したデータフレームはcsvで保存しておきましょう。
今後は、このcsvファイルを使って、さまざまな自然言語処理を行なっていきたいと思います。

Discussion

yuki3517yuki3517

1点お聞きしたいことがあり、コメント失礼いたします。
「2.有価証券報告書が入ったzipファイルを取得する」のところで

codelist_df = pd.read_csv("/content/edinetcodelist.csv",index_col=0)

というコードがあると思うのですが、この「edinetcodelist.csv」はどこから取得したものか教えていただけますでしょうか。
他の記事等見逃していたら申し訳ありません。

osn_Lofiosn_Lofi

コメントありがとうございます‼️😊

こちらは、以下の記事で作成したファイルになります
https://zenn.dev/robes/articles/bb7ea6202af81c

ただ、業種は別で取得したものかもしれません
(業種以外は上記の記事で取得できると思います)

やってみて、分からなかったら、また聞いてください‼️

よろしくお願いします

yuki3517yuki3517

早速のお返事ありがとうございます!
この記事も参考にさせて頂きました。

まさにこのcsvの中の、業種の取得方法をお聞きしたかったのですが
詳しく教えて頂くことは可能でしょうか?

osn_Lofiosn_Lofi

もちろんです。

https://disclosure2.edinet-fsa.go.jp/weee0010.aspx
の下の方にEDINETコードリストがあります。

これをダウンロードしてください。

CSVファイルの中に、EDINETコードと提出者業種がありますので、上で取得したファイルと紐づけることができます。

よろしくお願いします。

yuki3517yuki3517

できました!とても丁寧にありがとうございます。
他の記事も拝見しており、またご質問させていただくかもしれませんが
その際はどうぞよろしくお願いします。