Pythonで高配当株を探す⑤(EDINETから有価証券報告書の取得)

Python

はじめに

以前、yahooqueryを用いた企業情報を取得するプログラムを作成しました。yahooqueryでは、一部の企業情報が未登録であったり、一部の情報や過去の情報が登録されてないことがありました。

先日、友人との会話で株の売り買いは、有価証券報告書を参考に行っており、以前作成したプログラムで過去の3~5年分のデータを確実に見れるようにしてほしい、との要望を受けました。その会話の中でEDINETについて話題に挙がりました。

EDINETについて

  • 金融庁が運営する『金融商品取引法に基づく有価証券報告書等の開示書類に関する電子開示システム』
  • 取得できる有価証券報告書の情報は日本国内の企業のみ
  • APIで有価証券報告書を取得できる

EDINETからAPIを用いて日本国内企業の有価証券報告書を取得し、有価証券報告書のデータを抽出するPythonプログラムを作成することにしました。今回は、下記について掲載しています。

  • EDINETのAPIキー取得
  • EDINETからAPIで有価証券報告書を取得するPythonプログラム

財務諸表のデータを抽出するPythonプログラムは次回掲載する予定です。

EDINETのアカウント作成

EDINETのAPIを利用するためには、アカウントを作成してAPIキーを発行する必要がります。ここではEDINETのアカウント作成方法を掲載しました。

事前準備1:Edgeのポップアップ設定

EDINETのアカウントを作成する際、ブラウザのEdgeを使用します。アカウントを作成する前にEdgeの「ポップアップとリダイレクト」の設定を実施しておく必要があります。

  1. Edgeを起動して、「…」をクリックして「設定」をクリックします。
  2. 設定の「Cookieとサイトのアクセス許可」を選択して「ポップアップとリダイレクト」を選択してください。
  3. 許可の「追加」を選択してください。
  4. サイトの追加で「https://api.edinet-fsa.go.jp」を入力して「追加」を選択してください。

以上でEdgeの設定は終了です。

事前準備2:EDINETアカウント登録の準備

アカウントを作成する前に下記2つを準備しておいてください。

  1. アカウント登録用のメールアドレス
  2. 多要素認証用の電話番号

EDINETアカウントの登録

  1. Edgeで下記リンクにアクセスしてください。サインインのページが表示されます。
    EDINETのログインサイト
  2. 「今すぐサインアップ」選択してください。
  3. メールアドレスを入力して「確認コードを送信」を選択します。
    入力したメールアドレスに確認コードが送信されます。
  4. メールアドレスに届いた確認コードを入力して「コードの確認」を選択してください。
  5. コードの確認後にパスワードを設定します。
    パスワードを設定して「作成」を選択してください。
  6. 多要素認証の登録を行います。
    国番号を「Japan(+81)]を選択し、電話番号を入力してください。
    「コードの送信」を選択すると、入力した電話番号にSMSでコードが送られます。
    「電話する」を選択すると、確認コードが表示されるので電話で確認コードをテンキーで入力します。
  7. 受領したコードを入力して「コードの確認」を選択します。

以上でEDINETアカウントの作成は終了です。

EDINETのAPIキーの発行

アカウント登録の「コードの確認」を終えると、APIキー発行画面が表示されます。
※「Edgeのポップアップブロックの許可」を実施していないとAPIキー発行画面は表示されません
「Edgeのポップアップブロックの許可」を実施してアカウントを登録を実施し直してください。

  1. 氏名と電話番号を入力して「連絡先登録(Save)」を選択してください。
  2. 保存確認のダイアログが表示されるので「OK」を選択してください。
    保存が完了するとAPIキーが表示されます。

以上でAPIキーの発行は終了です。

EDINETからAPIで提出書類一覧と有価証券報告書を取得

EDINETからAPIで提出書類一覧を取得するプログラムを作成する前に、発行したAPIキーでEDINETから提出書類一覧と有価証券報告書取得してみます。

提出書類一覧を取得するリクエストURLについて説明します。リクエストURLはリクエストのエンドポイントとパラメータで構成されています。

リクエストのエンドポイント(リクエストを投げる先)

リクエストのエンドポイントは固定です。URLは下記となります。

https://api.edinet-fsa.go.jp/api/v2/documents.json?

リクエストパラメータ

設定できるパラメータは3つあります。下記の表に整理しました。
dateを変更することで指定した日付に提出された提出書類一覧を取得することができます。

パラメータ 項目 必須 設定内容
datte 提出された日付 必須 YYYY-MM-DD
tyep 取得情報 1:メタデータのみを取得
2:提出書類一覧及びメタデータを取得
Subscription-Key APIキー 必須 取得したAPIキー

リクエストURL

下記のリクエストURLは2023年6月28日に提出された書類一覧を取得することができます。
リクエストURLの最後の「XXXXXX」を取得したAPIキーに書き換えて、ブラウザでアクセスすると提出書類一覧をブラウザに表示することができます。

https://api.edinet-fsa.go.jp/api/v2/documents.json?date=2023-06-28&type=2&Subscription-Key=XXXXXXX

<参考:取得した提出書類一覧の抜粋>

{
    "metadata": {
        "title": "提出された書類を把握するためのAPI",
        "parameter": {
            "date": "2023-06-28",
            "type": "2"
        },
        "resultset": {
            "count": 1595
        },
        "processDateTime": "2023-10-03 00:05",
        "status": "200",
        "message": "OK"
    },
    "results": [
        {
            "seqNumber": 1,
            "docID": "S100R698",
            "edinetCode": "E03664",
            "secCode": "84160",
            "JCN": "4490001000608",
            "filerName": "株式会社高知銀行",
            "fundCode": null,
            "ordinanceCode": "010",
            "formCode": "030000",
            "docTypeCode": "120",
            "periodStart": "2022-04-01",
            "periodEnd": "2023-03-31",
            "submitDateTime": "2023-06-28 09:00",
            "docDescription": "有価証券報告書-第143期(2022/04/01-2023/03/31)",
            "issuerEdinetCode": null,
            "subjectEdinetCode": null,
            "subsidiaryEdinetCode": null,
            "currentReportReason": null,
            "parentDocID": null,
            "opeDateTime": null,
            "withdrawalStatus": "0",
            "docInfoEditStatus": "0",
            "disclosureStatus": "0",
            "xbrlFlag": "1",
            "pdfFlag": "1",
            "attachDocFlag": "1",
            "englishDocFlag": "0",
            "csvFlag": "1",
            "legalStatus": "1"
        },
        {
            "seqNumber": 2,
            "docID": "S100QOT0",
            "edinetCode": "E12444",
            "secCode": null,
            "JCN": "8010001114914",
            "filerName": "三井住友トラスト・アセットマネジメント株式会社",
            "fundCode": "G03571",
            "ordinanceCode": "030",
            "formCode": "995000",
            "docTypeCode": "180",
            "periodStart": null,
            "periodEnd": null,
            "submitDateTime": "2023-06-28 09:00",
            "docDescription": "臨時報告書(内国特定有価証券)",
            "issuerEdinetCode": null,
            "subjectEdinetCode": null,
            "subsidiaryEdinetCode": null,
            "currentReportReason": "第29条第2項第4号",
            "parentDocID": null,
            "opeDateTime": null,
            "withdrawalStatus": "0",
            "docInfoEditStatus": "0",
            "disclosureStatus": "0",
            "xbrlFlag": "1",
            "pdfFlag": "1",
            "attachDocFlag": "0",
            "englishDocFlag": "0",
            "csvFlag": "1",
            "legalStatus": "1"
        },

EDINETからAPIで有価証券報告書を取得

APIのリクエストURLに書類管理番号(docID)を指定することで、有価証券報告書をzipでダウンロードすることができます。

<参考:docIDとは>

  • EDINETにおいて提出された書類(主に企業の有価証券報告書や財務諸表など)を一意に識別するための識別子。
  • 企業がEDINETに提出した書類ごとに一意に割り当てられる一連の文字列または数字から構成されている。

提出された書類には任意の書類管理番号(docID)が割り当てられています。
書類管理番号(docID)は提出書類一覧から確認することができます。
提出された書類情報が有価証券報告書であるかを確認するための項目は下記となります。

項目名 項目ID コード値
府令コード ordinanceCode 010
様式コード formCode 030000

前項目で取得した提出書類一覧の一部を再掲します。8行目に「ordinanceCode」、9行目に「formCode」の記載があります。2行目に有価証券報告書を取得するときに必要な「docID」の記載があります。

            "seqNumber": 1,
            "docID": "S100R698",
            "edinetCode": "E03664",
            "secCode": "84160",
            "JCN": "4490001000608",
            "filerName": "株式会社高知銀行",
            "fundCode": null,
            "ordinanceCode": "010",
            "formCode": "030000",
            "docTypeCode": "120",
            "periodStart": "2022-04-01",
            "periodEnd": "2023-03-31",
            "submitDateTime": "2023-06-28 09:00",
            "docDescription": "有価証券報告書-第143期(2022/04/01-2023/03/31)",

その他の提出書類を取得したい場合は、様式コードリストは下記から様式コードリストをダウンロードして内容を確認してみてください。

EDINETガイド

<参考:様式コードリスト>

リクエストのエンドポイント(リクエストを投げる先)

リクエストのエンドポイントは固定です。URLは下記となります。
書類管理番号は、提出書類一覧のdocIDに記載されています。

https://api.edinet-fsa.go.jp/api/v2/documents/"書類管理番号"

リクエストパラメータ

dateを変更することで指定した日付に提出された提出書類一覧を取得することができます。

パラメータ 項目 必須 設定内容
type 取得情報 1:提出文書及び監査報告書を取得
2:PDFを取得
3:代替書面、添付文書を取得
4:英文ファイルを取得
5:CSVを取得
Subscription-Key APIキー 必須 取得したAPIキー

リクエストURL

下記のリクエストURLは2023年6月28日に提出された株式会社高知銀行の有価証券報告書を取得することができます。

リクエストURLの最後の「XXXXXX」を取得したAPIキーに書き換えて、ブラウザでアクセスすると株式会社高知銀行の有価証券報告書をzipでダウンロードできます。

https://api.edinet-fsa.go.jp/api/v2/documents/S100R698?type=1&Subscription-Key=XXXXXX

プログラムの概要

プログラムの概要をまとめました。

  • 指定した期間にEDINETへ提出された有価証券報告書を取得する
  • 証券コードを持っている会社の有価証券報告書のみを取得する
  • 取得した有価証券報告書はzipで指定した場所に保存する
  • 保存した有価証券報告書のzipを指定した場所に展開する

取得したzipと生成したファイルの保存場所

初期設定の状態でプログラムを実行すると、下記フォルダに日付(yyyymmdd)でフォルダを作成してファイルを生成、配置します。

C:\temp\data\zip\yyyymmdd

取得したzipの展開場所

初期設定の場合、zipの保存場所フォルダの「extract」フォルダを作成して展開します。

C:\temp\data\zip\yyyymmdd\extract

取得するzipの概要

有価証券報告書のzipを取得した場合、zip内のフォルダ構成は下記となっています。

会社名
 └─XBRL
     ├─AuditDoc
     └─PublicDoc
フォルダ名 内容
AuditDoc 監査報告書
PublicDoc 提出本文書

有価証券報告書の情報が記載されているXBRLファイルはPublicDoc配下に保存されています。
XBRLファイルの名前は下記となります。

jpcrp030000-asr-001_"edinetCode"-000_"決算日"_01_"提出日".xbrl

<参考:edinetCodeとは>

  • EDINETが開示書類等提出者に付与する 6 桁の英数字。
  • 日本の上場企業や金融機関、その他金融市場関連の組織を一意に識別するためのコード。
  • このコードは、EDINETシステム内で企業や組織を一意に特定するために使用。

EDINET タクソノミ用語集

生成するCSVファイルの説明

プログラムを実行すると、EDINETに接続して有価証券報告書を提出した企業の一覧を取得します。取得した一覧は「有価証券報告書を提出した企業の一覧.csv」に保存します。

「有価証券報告書を提出した企業の一覧.csv」のカラム情報

項目名 内容サンプル
1列 会社名 株式会社エフティグループ
2列 書類名 有価証券報告書-第38期(2022/4/01-2023/03/31)
3列 docID S100R7YK
4列 証券コード 27630
5列 EDINETコード E03405
6列 決算期 2023/3/31
7列 提出日 2023/6/30

<参考:CSVファイルの内容>

プログラムの実行環境

実行環境のパッケージのバージョンは下記となります。

パッケージ バージョン
python 3.10.9
pandas 1.5.2
requests 2.28.1

プログラムの内容

コードは下記となります。

import datetime
import os
import pandas as pd
import requests
import time
import zipfile


class XBRLDownloader:
    def __init__(self, xbrl_zip_base_path, subscription_key):
        """
        XBRLDownloaderクラスのコンストラクタ

        :param xbrl_zip_base_path:str
            XBRL情報のzipファイルを保存する大元のフォルダパス
        :param subscription_key:str
            EDINETに接続する際に使用するAPIキー
        """
        self.xbrl_zip_base_path = xbrl_zip_base_path
        self.xbrl_zip_folder_path = self.create_folder_to_save_xbrl_zip()
        self.subscription_key = subscription_key

    def create_date_list(self, start_date, end_date):
        """
        指定した開始日から終了日(含む)までの日付のリストを生成します。

        :param start_date:datetime.date
            期間の開始日
        :param end_date:datetime.date
            期間の終了日
        :return:list
            開始日から終了日までの日付が格納されたリスト
        """
        period = end_date - start_date
        period = int(period.days)
        date_list = []
        for d in range(period):
            date = start_date + datetime.timedelta(days=d)
            date_list.append(date)

        date_list.append(end_date)
        return date_list

    def create_info_list(self, date_list):
        """
        提出書類の一覧を取得し、特定の条件を満たす書類情報一覧をリストに格納して返します。

        :param date_list:list
            提出書類一覧を取得する日付のリスト
        :return:list
            特定の条件を満たす書類情報を含むリスト
        """
        info_list = []
        for date in date_list:
            print("{} の提出書類の一覧を取得します".format(date))
            url = "https://api.edinet-fsa.go.jp/api/v2/documents.json"
            params = {
                "date": date,
                "type": 2,
                "Subscription-Key": self.subscription_key
            }
            res = requests.get(url, params=params)
            json_data = res.json()
            time.sleep(1)

            for num in range(len(json_data["results"])):
                # ordinanceCode==010かつformCode==030000で有価証券報告書を示す
                # secCodeが無い会社は上場していない
                # ifでEDINETに提出された上場会社の有価証券報告書の種類管理番号などを取得する
                if (json_data["results"][num]["ordinanceCode"] == "010" and
                        json_data["results"][num]["formCode"] == "030000" and
                        json_data["results"][num]["secCode"] is not None):
                    edi = {
                        '会社名': json_data["results"][num]["filerName"],
                        '書類名': json_data["results"][num]["docDescription"],
                        'docID': json_data["results"][num]["docID"],
                        '証券コード': json_data["results"][num]["secCode"],
                        'edinetコード': json_data["results"][num]["edinetCode"],
                        '決算期': json_data["results"][num]["periodEnd"],
                        '提出日': date
                    }
                    info_list.append(edi)

        return info_list

    def create_folder_to_save_xbrl_zip(self):
        """
        XBRL情報のZIPファイルを保存するフォルダを作成します。
        現在の日付を取得し、それを使用して年月日形式のフォルダ名を生成します。
        生成されたフォルダは、XBRLのZIPファイルの保存先として利用されます。
        すでに存在する場合は無視されます。

        :return:str
            生成されたフォルダのパス
        """
        current_date = datetime.datetime.now()
        folder_name = current_date.strftime("%Y%m%d")
        folder_path = os.path.join(self.xbrl_zip_base_path, folder_name)

        if not os.path.exists(folder_path):
            os.makedirs(folder_path)

        return folder_path

    def download_xbrl_zip(self, docid, company_name):
        """
         指定された企業名のXBRL情報をダウンロードします。
         ダウンロードされたファイルは xbrl_zip_folder_path フォルダに保存されます。

        :param docid:str
            ダウンロード対象のXBRL文書のID
        :param company_name:str
            会社名
        :return:None
        """
        print("{} のXBRL情報をダウンロードします".format(company_name))
        url = "https://api.edinet-fsa.go.jp/api/v2/documents/" + docid
        params = {
            "type": 1,
            "Subscription-Key": self.subscription_key
        }
        res = requests.get(url, params=params)
        filename = os.path.join(self.xbrl_zip_folder_path, f"{company_name}.zip")

        if res.status_code == 200:
            with open(filename, 'wb') as f:
                for chunk in res.iter_content(chunk_size=1024):
                    f.write(chunk)

    def extract_zip(self, filename):
        """
        ZIPファイルを展開します。
        指定されたZIPファイルを展開し、指定されたフォルダに解凍します。

        :param filename:str
            展開対象のZIPファイルのファイル名
        :return:None
        """
        if filename.endswith('.zip'):
            zip_file_path = os.path.join(self.xbrl_zip_folder_path, filename)
            with zipfile.ZipFile(zip_file_path, 'r') as zip_ref:
                zip_ref.extractall(os.path.join(self.extract_folder_path, os.path.splitext(filename)[0]))

    def create_extract_folder(self):
        """
        ZIPファイルを展開するためのフォルダを作成します。

        :return:None
        """
        self.extract_folder_path = os.path.join(self.xbrl_zip_folder_path, 'extract')

        if not os.path.exists(self.extract_folder_path):
            os.makedirs(self.extract_folder_path)

    def download_xbrl(self, start_date, end_date):
        """
        指定された期間内に提出された企業の有価証券報告書のXBRL情報をダウンロードします。

        :param start_date:datetime.date
            ダウンロード期間の開始日
        :param end_date: datetime.date
            ダウンロード期間の終了日
        :return: None
        """
        date_list = self.create_date_list(start_date, end_date)
        info_list = self.create_info_list(date_list)

        # 有価証券報告書を提出した企業の一覧をCSVに保存する
        df_info_list = pd.DataFrame(info_list)
        list_securities_reports_csv_path = os.path.join(self.xbrl_zip_folder_path, '有価証券報告書を提出した企業の一覧.csv')
        df_info_list.to_csv(list_securities_reports_csv_path, index=False, encoding='cp932')

        for info in info_list:
            time.sleep(1)
            self.download_xbrl_zip(info['docID'], info['会社名'])

    def extract_xbrl(self):
        """
        ダウンロードした有価証券報告書のXBRL情報のzipファイルを展開します。

        :return:None
        """
        self.create_extract_folder()

        for filename in os.listdir(self.xbrl_zip_folder_path):
            self.extract_zip(filename)


if __name__ == "__main__":
    # XBRL情報の保存先フォルダのパス
    xbrl_zip_base_path = r'C:/temp/data/zip/'

    # APIキー
    subscription_key = "XXXXXXXXXXXXXXXXXXXXXXXXXX"

    # ダウンロード期間の開始日と終了日を指定する
    start_date = datetime.date(2023, 6, 28)
    end_date = datetime.date(2023, 6, 30)

    # XBRLDownloaderクラスのインスタンスを作成する
    downloader = XBRLDownloader(xbrl_zip_base_path, subscription_key)

    # XBRL情報をダウンロードして展開する
    downloader.download_xbrl(start_date, end_date)
    downloader.extract_xbrl()

プログラム実行の準備作業

プログラムを実行する前に準備作業として、下記を実施してください。

取得したXBRL情報の保存場所の設定

保存場所は下記を更新することで変更することができます。

    # XBRL情報の保存先フォルダのパス
    xbrl_zip_base_path = r'C:/temp/data/zip/'

APIキーの設定

APIキーは下記を更新することで変更することができます。

    # APIキー
    subscription_key = "XXXXXXXXXXXXXXXXXXXXXXXXXX"

ダウンロード期間の開始日と終了日を指定

ダウンロード機関の開始日と終了日は下記を更新することで変更することができます。

    # ダウンロード期間の開始日と終了日を指定する
    start_date = datetime.date(2023, 6, 28)
    end_date = datetime.date(2023, 6, 30)

プログラムの実行方法

  1. プログラムのコードを「任意のファイル名.py」で保存してください。
    ここでは「EdinetReportFetchr.py」としています。
  2. Pythonの実行環境を開いてください。
  3. 下記のコマンドで実行します。
> python EdinetReportFetchr.py

取得するzipファイル(XBRLファイル)の数によって、実行完了までの時間は変わります。1000個のzipファイルの取得には、約20分ほどかかりました。

プログラムの補足

下記の関数で「ordinanceCode」と「formCode」を設定して有価証券報告書の書類管理番号を取得しています。「ordinanceCode」と「formCode」を変更することで有価証券報告書以外のデータを取得することも可能です。

    def create_info_list(self, date_list):
        """
        提出書類の一覧を取得し、特定の条件を満たす書類情報一覧をリストに格納して返します。

        :param date_list:list
            提出書類一覧を取得する日付のリスト
        :return:list
            特定の条件を満たす書類情報を含むリスト
        """
        info_list = []
        for date in date_list:
            print("{} の提出書類の一覧を取得します".format(date))
            url = "https://api.edinet-fsa.go.jp/api/v2/documents.json"
            params = {
                "date": date,
                "type": 2,
                "Subscription-Key": self.subscription_key
            }
            res = requests.get(url, params=params)
            json_data = res.json()
            time.sleep(1)

            for num in range(len(json_data["results"])):
                # ordinanceCode==010かつformCode==030000で有価証券報告書を示す
                # secCodeが無い会社は上場していない
                # ifでEDINETに提出された上場会社の有価証券報告書の種類管理番号などを取得する
                if (json_data["results"][num]["ordinanceCode"] == "010" and
                        json_data["results"][num]["formCode"] == "030000" and
                        json_data["results"][num]["secCode"] is not None):
                    edi = {
                        '会社名': json_data["results"][num]["filerName"],
                        '書類名': json_data["results"][num]["docDescription"],
                        'docID': json_data["results"][num]["docID"],
                        '証券コード': json_data["results"][num]["secCode"],
                        'edinetコード': json_data["results"][num]["edinetCode"],
                        '決算期': json_data["results"][num]["periodEnd"],
                        '提出日': date
                    }
                    info_list.append(edi)

        return info_list

次回、試したいこと

取得したXBRLデータから、売上や利益などのデータを抽出する処理を作りたいと思います。余力があったら、以前作成したレポート生成機能の改修も行います。レポートの見た目がダサいので。。。

コメント

タイトルとURLをコピーしました