リニューアル後のEDINETからEDINETコードリストを取得する

## EDINET、EDINETコードリストについて

https://disclosure2.edinet-fsa.go.jp/WEEK0010.aspx

EDINET、EDINETコードリストについて、詳細は EDINET の[操作ガイド等](https://disclosure2dl.edinet-fsa.go.jp/guide/static/disclosure/WZEK0110.html)を参照してください。  
本稿で扱う EDINETコードリストは上場企業の一覧などを簡単に取得できることから機械的な取得手段を解説する記事が Qiita 上含め、複数存在します。

https://qiita.com/9uant/items/d15f8b97a9b2b84671fd

## EDINET のサイトリニューアルとそれによって発生した問題

しかし、EDINET サイトは 2023 年 1 月 4 日に全面的にリニューアルされてしまいました。  
旧EDINET はサイトの拡張子が jsp だったのが新EDINETでは aspx になったのでサーバーサイドは Java から C#に全面的に作り変えられたようです。
また、画面も以前と同じように見えますが、似ているようで一部違うところがあり、どうやら UI が同じになるように一から書き換えているようです。

技術的な面だけでなく仕様も変更されています。一部の書類の閲覧期間が 5 年から 10 年になるなどの変更が入りました。

困ったことに、このリニューアルによって今まで動いていた EDINET コードリストダウンロードスクリプトが動かなくなってしまいました。

これまでは、[EDINET API 仕様書](https://disclosure2dl.edinet-fsa.go.jp/guide/static/disclosure/download/ESE140192.zip)の「3-1 参考資料 EDINET コードリスト、ファンドコードリスト」にあるように、旧 EDINET のダウンロードページに存在した

https://disclosure.edinet-fsa.go.jp/E01EW/BLMainController.jsp?uji.bean=ee.bean.W1E62071.EEW1E62071Bean&uji.verb=W1E62071InitDisplay&TID=W1E62071&PID=W0EZ0001&SESSIONKEY=&lgKbn=2&dflg=0&iflg=0

というリンクに Get リクエストを送ることで zip ファイルをダウンロードすることができました。  
しかし、EDINET リニューアルに伴い、この URL は存在しなくなりました。  
画面の見た目は同じなのですが、かつての EDINET コードリストダウンロードリンクは、現在、

```html
<a href="javascript:void(0);" onclick="onDownloadEdinet(); return false;">ダウンロード</a>
```

というリンクになっています。  
単純なリンクではなくなってしまいました。

開発者ツールで確認するとこのリンクが押されると JavaScript から POST リクエストが送信されるようなのですが、それで単純に zip ファイルをダウンロードするわけでもないようであり、仕組みを理解するのは私にはできませんでした……。  
ブラウザ + selenium の組み合わせで直接クリックすれば確実なので、その方式で実装しました。言語はサンプルプログラムが多い Python を使いました。

## 修正後のダウンロードスクリプト

結果的に次のようなコードでダウンロードできました。

seleniumのDockerコンテナ（[selenium/standalone-chrome:108.0-chromedriver-108.0-grid-4.7.2-20221219](https://hub.docker.com/layers/selenium/standalone-chrome/108.0-chromedriver-108.0-grid-4.7.2-20221219/images/sha256-15835b1b173cc21415e404ad295a876dc75fa107f48b5ef4ad8314b68bd1abd6?context=explore)）内で実行しています。

seleniumのバージョン4.7.2  
Chromeのバージョンは108.0  
Pythonのバージョンは3.8.10
が前提です。

```Python
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
import time


def main():
    # ここから環境依存コード
    options = webdriver.ChromeOptions()
    options.add_argument("--headless")
    options.set_capability("browserVersion", "108.0")

    prefs = {}
    prefs["profile.default_content_settings.popups"] = 0
    prefs["download.default_directory"] = "/home/seluser/downloads"
    options.add_experimental_option("prefs", prefs)


    try:
        driver = webdriver.Chrome(options=options)
        # ここまで環境依存コード

        # EDINETの「EDINETタクソノミ及びコードリストダウンロード指定画面」へ遷移
        driver.get("https://disclosure2.edinet-fsa.go.jp/weee0010.aspx")

        # 「JavaScriptが読み込まれたこと」を直接waitする手段はないようなので、代わりに「リンクがクリックできるようになったこと」を待機する
        WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, "#GridContainerRow_0001 a"))
        )
        # リンクをクリックすると実行されるJavaScriptをseleniumから直接実行する
        driver.execute_script("onDownloadEdinet()")

        # これでもOK。`#GridContainerRow_0001 a` はダウンロードボタンのCSSセレクタ
        # element = driver.find_element(By.CSS_SELECTOR, "#GridContainerRow_0001 a")
        # element.click()

        # ファンドコードリストのダウンロードも同様に可能
        WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, "#GridContainerRow_0002 a"))
        )
        driver.execute_script("onDownloadFund()")

        # JavaScriptの実行を待つ仕組みはseleniumになさそうだったため、単純にsleepする。
        time.sleep(60)

        driver.close()
    finally:
        driver.quit()


if __name__ == "__main__":
    main()
```

環境依存コードとある部分は人によって変わる可能性がある部分になります。Chrome へのパスが通っているかどうかなどで記述の仕方が変わります。また、私は Docker で実行しているのですが、そうでない場合は`download.default_directory`などを設定する必要がないかもしれません。  
詳しくは

https://selenium-python.readthedocs.io/index.html

などのドキュメントを参照してください。

処理内容はコード中のコメントで示しましたが、

```Python
element = driver.find_element(By.CSS_SELECTOR, "#GridContainerRow_0001 a")
element.click()
```

とするとダウンロードボタンをクリックし、zip ファイルをダウンロードすることができます。

ただ、a タグを見ると、クリック（`onclick`）したときに、`onDownloadEdinet()`という JavaScript の関数が実行されていることがわかります。  
これを直接実行してしまったほうがより端的な実装になるかと思い、

```Python
driver.execute_script("onDownloadEdinet()")
```

として直接 JavaScript を実行する方式を使うことにしました。これでもクリックしたときと同様に zip ファイルをダウンロードできます。

ETF を含めた有価証券のリストであるファンドコードリストも同様に取得できます。実行する JavaScript の関数は `onDownloadFund()` になります。

## その他

もしかしたら EDINET API でも同じ情報の取得が可能なのでしょうか……？　一見なさそうなので確実に可能な selenium を使う方法を採用してしまいました。  
もしほかに良い方法があればご教示いただきたいです。

Python も selenium も普段使わないので前述のコードは改善の余地があると思います。

### ダウンロードできる CSV ファイルの形式について

ダウンロード方法は変わりましたが、ダウンロードできる CSV の形式は同じと思われます。

ただ、気が付いた範囲だと、「決算日」の先頭に入っていたスペースが無くなっていました。  
困る人は殆どいないと思いますが、せっかく気付いたので念のため記載しておきます。

![diff.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273742/4c5e315e-6138-ed00-0f87-84096c6aa935.png)

他にもまだ差分がある可能性はあります。