EDINET APIを利用して、企業情報（XBRLデータ）を自動で集めてみよう(4/10)

Advent Calendar ４回目の投稿では、EDINETで開示されているXBRLで記述されたデータをプログラムで集めてみます。

（本記事のプログラムは、一切の保証なく、現状で提供されるものであり、XBRLJapanは、本プログラムの利用に伴って発生した不利益や問題について、原因のいかんを問わず、一切の責任を負わないものとします。）

※2024/04/09：
本記事で紹介しているEDINET API（Version1）が2024年3月29日で利用が終了になりました。
代わりに、EDINET API（Version2）が2024年4月1日から利用が開始されています。
CSVファイルでの提供機能追加などで便利になりましたが、使い方はVesion1とほぼ同じです。また、認証登録が必要です。お手数ですが、詳しくはEDINETサイト等をご確認ください。

# 1. EDINET APIとは
EDINET APIは、EDINETの画面からではなく、`プログラムを介してEDINETのデータベースから効率的に(XBRL)データを取得できるAPI`です。EDINET APIにより、EDINET利用者は効率的に開示情報を取得することが可能となります。APIの利用時には、EDINETのページにある**[利用規約](https://disclosure.edinet-fsa.go.jp/download/ESE140191.pdf)**を事前にご確認ください。

<img width="450" alt="スクリーンショット 2019-12-01 16.47.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/500494/3da88c04-d25b-d82a-28dd-594cf15edfd2.png">

# 2. EDINET APIでXBRLデータを集めてみる
## 2.1 プログラム概要
本プログラムは、収集期間を指定することで、当該期間にEDINETで開示された有価証券報告書に対応するXBRLデータを、EDINET APIを介してダウンロードするPython言語のプログラムです。（「3. ソースコード」に全コード記載）[**EDINET APIの詳細仕様**](https://disclosure.edinet-fsa.go.jp/EKW0EZ0015.html)は、EDINETサイトからご確認ください。なお、四半期/半期報告書、訂正有価証券報告書には対応していません。

## 2.2 事前準備
以下内容について、プログラム実行前に対応してください。また、その他ライブラリ（requests、datetimeなど）のインストールなども事前に実施が必要です。

### 2.2.1 エンドポイントの設定
エンドポイントは2019年12月時点のものです。最新のバージョン(ex. v1)であるかは、その都度確認ください。
`https://disclosure.edinet-fsa.go.jp/api/v1/documents.json`

<img width="450" alt="スクリーンショット 2019-12-01 18.01.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/500494/6c2f69a1-2f72-84f3-ac32-02943624730c.png">

### 2.2.2 XBRL収集期間の設定
XBRLデータを収集したい期間に応じて、`start_date`（収集開始日）と`end_date`（収集終了日）の日付を変更してください。start_dateは、プログラム実行日から5年前までの日付を指定することができます。
`start_date = datetime.date(2019, 11, 1)`
`end_date = datetime.date(2019, 11,30)`

### 2.2.3 Proxyの設定
ネットワーク環境を踏まえProxyを設定します。Proxyが不要であれば、`proxies=proxies`は削除してください。
`"http_proxy" : "http://username:password@proxy.example.com:8080"`
`"https_proxy" : "https://username:password@proxy.example.com:8080"`

### 2.2.4 出力フォルダの決定
XBRLデータがダウンロードされるフォルダ先を決めましょう。
`C://Users//xxx//Desktop//xbrlReport//SR//`

## 2.3 実行結果
収集開始日と収集終了日を定めることで、`day_list`が作成されます。

```python:Code1
start_date = datetime.date(2019, 11, 1)
end_date = datetime.date(2019, 11,30)
```

```:Result1
day_list [datetime.date(2019, 11, 1), datetime.date(2019, 11, 2), datetime.date(2019, 11, 3), datetime.date(2019, 11, 4), datetime.date(2019, 11, 5), datetime.date(2019, 11, 6), datetime.date(2019, 11, 7), datetime.date(2019, 11, 8), datetime.date(2019, 11, 9), datetime.date(2019, 11, 10), datetime.date(2019, 11, 11), datetime.date(2019, 11, 12), datetime.date(2019, 11, 13), datetime.date(2019, 11, 14), datetime.date(2019, 11, 15), datetime.date(2019, 11, 16), datetime.date(2019, 11, 17), datetime.date(2019, 11, 18), datetime.date(2019, 11, 19), datetime.date(2019, 11, 20), datetime.date(2019, 11, 21), datetime.date(2019, 11, 22), datetime.date(2019, 11, 23), datetime.date(2019, 11, 24), datetime.date(2019, 11, 25), datetime.date(2019, 11, 26), datetime.date(2019, 11, 27), datetime.date(2019, 11, 28), datetime.date(2019, 11, 29), datetime.date(2019, 11, 30)]
2
```

`day_list`を使ってループ処理を行い、各日付ごとに、`url`(エンドポイント)、`params`(日付情報)、必要に応じて`proxies`（Proxy情報）を設定し、`requests.get(url, params=params ,proxies=proxies)`を実行することで、`res`（Responseオブジェクト）を取得します。`url`は書類一覧APIに該当するエンドポイントを指定します。`params`で`"type": 2`を指定し提出書類一覧を取得している理由は、後続の処理で、有価証券報告書を特定するためです。

```python:Code2

for index,day in enumerate(day_list):
    url = "https://disclosure.edinet-fsa.go.jp/api/v1/documents.json"
    params = {"date": day, "type": 2}

    proxies = {
        "http_proxy" : "http://username:password@proxy.example.com:8080/",
        "https_proxy" : "https://username:password@proxy.example.com:8080/"
    }

　　res = requests.get(url, params=params ,proxies=proxies)

```

`res`はEDINET API仕様書.pdfの2-1-2-1 書類一覧API（メタデータ）で構造が定義されています。以下は、2019.11.1の`day`に対応した`res`の中身です。

```:Result2


2019-11-01
{
    "metadata": {
        "title": "提出された書類を把握するためのAPI",
        "parameter": {
            "date": "2019-11-01",
            "type": "2"
        },
        "resultset": {
            "count": 315
        },
        "processDateTime": "2019-12-05 00:00",
        "status": "200",
        "message": "OK"
    },
    "results": [
        {
            "seqNumber": 1,
            "docID": "S100H5LU",
            "edinetCode": "E12422",
            "secCode": null,
            "JCN": "4010001046310",
            "filerName": "しんきんアセットマネジメント投信株式会社",
            "fundCode": "G03385",
            "ordinanceCode": "030",
            "formCode": "07A000",
            "docTypeCode": "120",
            "periodStart": "2018-08-07",
            "periodEnd": "2019-08-06",
            "submitDateTime": "2019-11-01 09:00",
            "docDescription": "有価証券報告書（内国投資信託受益証券）－第17期(平成30年8月7日－令和1年8月6日)",
            "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"
        },
        
```

resの`results`で提出書類一覧がリスト管理されているので、`results`を使ってループ処理を行います。その後、resultsの提出書類ごとに、`ordinanceCode`（府令コード）と `form_code`（様式コード）を取得します。今回は有価証券報告書を対象とするため、`ordinanceCode`が010、`form_code`が030000の提出書類のみ処理を行うようにしました。該当した提出書類の、`docID`（書類管理番号）を取得し、`securities_report_doc_list`(有価証券報告書のリスト)に格納しておきます。

```python:Code3

for num in range(len(json_data["results"])):
        ordinance_code= json_data["results"][num]["ordinanceCode"]
        form_code= json_data["results"][num]["formCode"]

        if ordinance_code == "010" and  form_code =="030000" :
            securities_report_doc_list.append(json_data["results"][num]["docID"])

```
これにより、有価証券報告書に対応する`docID`のリストが作成できました。

```:Result3
number_of_lists： 77
get_list： ['S100H8TT', 'S100HE9U', 'S100HC6W', 'S100HFA0', 'S100HFBC', 'S100HFB3', 'S100HG9S', 'S100HG62', 'S100HGJL', 'S100HFMG', 'S100HGM1', 'S100HGMZ', 'S100HGFM', 'S100HFC2', 'S100HGNQ', 'S100HGS3', 'S100HGYR', 'S100HGMB', 'S100HGKE', 'S100HFJG', 'S100HGTC', 'S100HH1G', 'S100HH9I', 'S100HGTF', 'S100HHAL', 'S100HHC0', 'S100HFIB', 'S100HH1I', 'S100HH36', 'S100HHDF', 'S100HH9L', 'S100HHGB', 'S100HHGJ', 'S100HHCR', 'S100HHJJ', 'S100HHH0', 'S100HHLH', 'S100HHL6', 'S100HHD4', 'S100HHM7', 'S100HHL9', 'S100HHN6', 'S100HHO8', 'S100HHHV', 'S100HHE3', 'S100HGB5', 'S100HHQ0', 'S100HHP5', 'S100HHMK', 'S100HHE6', 'S100HHPR', 'S100HHDA', 'S100HHR7', 'S100HHSB', 'S100HHML', 'S100HH9H', 'S100HH2F', 'S100H8W1', 'S100HHRP', 'S100HHTM', 'S100HHAF', 'S100HHUD', 'S100HHK9', 'S100HHT4', 'S100HHCI', 'S100HHXQ', 'S100HHO8', 'S100HHSS', 'S100HHRL', 'S100HI19', 'S100HHXS', 'S100HI1W', 'S100HHSP', 'S100HHN4', 'S100HI3J', 'S100HI3K', 'S100HI4G']

```

次は、当該リストを用いて、XBRLデータをダウンロードするコードです。`securities_report_doc_list`を使ってループ処理を行います。`url`は書類取得API（書類一覧APIではないことに注意）に該当するエンドポイントを指定します。`params`で`"type": 1`を指定することで、提出本文書及び監査報告書を取得することが可能です。`res`の`ステータスコード`が200(リクエスト成功時)のみ、XBRLデータをダウンロードしています。なお、5年以上前など、EDINETが保持する期間対象外の日時を指定した場合などに、404(リソースが存在しない)のステータスコードが返却されたりします。

```python:Code4
for index,doc_id in enumerate(securities_report_doc_list):
    url = "https://disclosure.edinet-fsa.go.jp/api/v1/documents/" + doc_id
    params = {"type": 1}
    filename = "C:\\Users\\XXX\\Desktop\\xbrlReport\\SR\\" + doc_id + ".zip"
    res = requests.get(url, params=params ,stream=True)

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

実行後、指定フォルダにzipファイルが77件ダウンロードされました。
<img width="283" alt="スクリーンショット 2019-12-01 17.52.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/500494/25f02fb4-2a59-f0b0-89c8-e2f3eea6df95.png">
zipファイルを解凍すると、お馴染みのAuditDocとPublicDocフォルダが出現します。以上で、XBRLデータのダウンロードが完了しました。
<img width="400" alt="スクリーンショット 2019-12-01 18.09.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/500494/dad0a7bb-28aa-7d53-0fcf-ae59ee8c5664.png">



# 3. ソースコード

```python
# -*- coding: utf-8 -*-
import requests
import datetime


def make_day_list(start_date, end_date):
    print("start_date：", start_date)
    print("end_day：", end_date)

    period = end_date - start_date
    period = int(period.days)
    day_list = []
    for d in range(period):
        day = start_date + datetime.timedelta(days=d)
        day_list.append(day)

    day_list.append(end_date)

    return day_list


def make_doc_id_list(day_list):
    securities_report_doc_list = []
    for index, day in enumerate(day_list):
        url = "https://disclosure.edinet-fsa.go.jp/api/v1/documents.json"
        params = {"date": day, "type": 2}

        proxies = {
            "http_proxy": "http://username:password@proxy.example.com:8080",
            "https_proxy": "https://username:password@proxy.example.com:8080"
        }

        res = requests.get(url, params=params, proxies=proxies)
        json_data = res.json()
        print(day)

        for num in range(len(json_data["results"])):

            ordinance_code = json_data["results"][num]["ordinanceCode"]
            form_code = json_data["results"][num]["formCode"]

            if ordinance_code == "010" and form_code == "030000":
                print(json_data["results"][num]["filerName"], json_data["results"][num]["docDescription"],
                      json_data["results"][num]["docID"])
                securities_report_doc_list.append(json_data["results"][num]["docID"])

    return securities_report_doc_list


def download_xbrl_in_zip(securities_report_doc_list, number_of_lists):
    for index, doc_id in enumerate(securities_report_doc_list):
        print(doc_id, ":", index + 1, "/", number_of_lists)
        url = "https://disclosure.edinet-fsa.go.jp/api/v1/documents/" + doc_id
        params = {"type": 1}
        filename = "C://Users//xxx//Desktop//xbrlReport//SR//" + doc_id + ".zip"
        res = requests.get(url, params=params, stream=True)

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

def main():
    start_date = datetime.date(2019, 11, 1)
    end_date = datetime.date(2019, 11, 30)
    day_list = make_day_list(start_date, end_date)

    securities_report_doc_list = make_doc_id_list(day_list)
    number_of_lists = len(securities_report_doc_list)
    print("number_of_lists：", len(securities_report_doc_list))
    print("get_list：", securities_report_doc_list)

    download_xbrl_in_zip(securities_report_doc_list, number_of_lists)
    print("download finish")


if __name__ == "__main__":
    main()

```

# 4. 有価証券報告書以外の報告書の集め方
今回は有価証券報告書を対象にしましたが、`ordinanceCode`と`form_code`を変更することで、他のXBRLで記述された報告書のデータも自動で収集することができます。例えば、四半期報告書であれば、`ordinanceCode`が010、`form_code`が043000の提出書類のみ処理を行う仕組みに変更すればよいです。以下に訂正も含め簡単に整理をしました。

〇有価証券報告書
`if ordinanceCode == "010" and  formCode =="030000" :`

〇訂正有価証券報告書
`if ordinanceCode == "010" and  formCode =="030001" :`

〇四半期報告書
`if ordinanceCode == "010" and  formCode =="043000" :`

〇訂正四半期報告書
`if ordinanceCode == "010" and  formCode =="043001" :`

全様式の府令コードと様式コードについては、[EDINET API関連資料（平成31年３月17日公開）](https://disclosure.edinet-fsa.go.jp/EKW0EZ0015.html)の[EDINET API仕様書]からダウンロードされるzipファイルに含まれている`別紙1_様式コードリスト.xlsx`を参照ください。
# 5. 問合せ先
本記事に関する問い合わせは、以下のメールアドレスまでお願いします。
e-mail：xbrl-tech-qa@xbrl.or.jp
（もちろん、qiita上でのコメントも歓迎します）

本メールアドレスは、qiitaの記事を執筆しているXBRLJapanの[開発委員会](https://www.xbrl.or.jp/modules/pico3/index.php?content_id=10)の問合せ窓口になります。
そのため、組織に関する一般的な問合せなどは内容によって回答できかねますが、XBRLに関する技術的な質問、意見、要望、助言等はお気軽にご連絡ください。
なお、委員会メンバが有志で対応しているため、回答に時間がかかることもありますが、ご了承ください。