ゼロから始めないXBRL解析(Arelleの活用)

# 1.はじめに
　XBRLは、金融庁(EDINET)、東証(TDnet)、国税庁(e-Tax)、日本銀行、環境省など多数の監督当局が採用した一方で、その複雑さゆえ、ネット上で密かにぷち炎上するような、知る人ぞ知るアンダーグラウンドな技術である。
本記事では、そんなXBRLを**簡単**かつ**仕様通り**に解析し、情報を取得してくる方法を記載する。解析をゼロから始めると泥沼に嵌るため、難解な技術やパズルが大好物という方以外は、本記事で紹介する方法などで楽をして解析することを推奨する。これにより、XBRL解析機能の車輪の再開発に係る労力を、Webサービス開発等に回せたならば幸い。

# 2.XBRLについて
　XBRLは、財務諸表やコーポレートガバナンス報告書など、レポートを電子化するためのXMLベースの言語。「インスタンス」と「タクソノミ」から成る。インスタンスは、報告の金額、数値、テキスト以外にも、コンテキスト情報（期、年度など）や単位情報が記述される。タクソノミは、インスタンスの構造・扱われ方などを定義する。**~~タクソノミで用いられる「xlink」が、XBRLを複雑化させた原因の一つであることは、ここでは混乱を招くため書かないでおく~~**

- その他詳しくは、[XBRLJapanのサイト](https://www.xbrl.or.jp/modules/pico7/index.php?content_id=1)を、どうぞ。
- もっと詳しく知りたい方は、XBRLJapanが発行している[FACTBOOK2016](https://www.xbrl.or.jp/uploads/fckeditor/download/factbook/2016/FACTBOOK2016_4.pdf)を、どうぞ。
- 英語が得意なあなたは、[XBRLInternational（略称XII）のサイト](https://www.xbrl.org/the-standard/what/an-introduction-to-xbrl/)を、どうぞ。


# 3.XBRL解析ソフトウェアについて
　EDINETやTDnetで開示されているXBRL形式の財務データを分析し、投資に活用するために、個人でXBRL解析ソフトウェア（XBRLを検証、処理できるソフトウェア）を開発する方もいるが、**仕様どおりに開発するのは難しい。**XBRLの各種仕様は、[XBRLInternational](https://specifications.xbrl.org/specifications.html)が公開している。日本で主に採用されている仕様は、[XBRL 2.1(Spec2.1)](http://www.xbrl.org/Specification/XBRL-2.1/REC-2003-12-31/XBRL-2.1-REC-2003-12-31+corrected-errata-2013-02-20.html)、[XBRL Dimensions 1.0](http://www.xbrl.org/specification/dimensions/rec-2012-01-25/dimensions-rec-2006-09-18+corrected-errata-2012-01-25-clean.html)、そして[Inline XBRL Specification 1.0](http://www.xbrl.org/specification/inlinexbrl-part1/rec-2010-04-20/inlinexbrl-part1-rec-2010-04-20+corrected-errata-2011-08-17.html)の３つ。基本仕様のXBRL2.1だけでも200頁近い大作。これを**仕様通りに開発するのは本当に難しい。**
なお、ソフトが仕様通りの挙動か否かは、XIIの各仕様のページにある「Conformance Suite」を利用し、検証するのが一番の近道。この検証に合格した上で、その他様々な手続きをパスすると[「XBRL Certified Software」](https://software.xbrl.org/)というXIIが認定するXBRLソフトウェアの称号を得ることができる。カッコいいロゴがその証。
![xbrl-certified-software-logo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/472427/42843135-17e7-8138-7505-f2c93b2fb0d3.png)
記事の途中であるが、概ね言いたいことが出てきたので、本記事の結論をここに書いておく。

<font color="Red">**『XBRLを”簡単”かつ”仕様通り”に解析し、情報を取得してくるには「XBRL Certified Software」を得ているソフトウェアを使うのがよい』**</font>

「XBRL Certified Software」は[このサイト](https://software.xbrl.org/)に全て記載されている。2019年8月時点では、10件あるようだ。（Report Consumption Softwareに該当するものをカウント）
しかし、そんな苦労して作ったソフトウェアなんだから、ライセンス料高いんでしょ？と思うかもしれない。・・・ある、この中にOSSがある。

# 4.Arelleについて
　[Arelle](https://software.xbrl.org/consume/mark-v-systems-arelle)は「XBRL Certified Software」を保有するOSS。つまり無償。
それでいて、他の有償のXBRL解析ソフトウェアと同様に殆どの（あるいは全ての）仕様をカバーしている。

【Arelleがカバーしている主な仕様】
- XBRL v2.1
- Dimensions v1.0
- Inline XBRL v1.1
- Formula v1.0

Arelleの[ギフハブ](https://github.com/Arelle/Arelle)はこちら。
特徴を羅列する。ライセンスは**Apache License 2.0**なので改修してビジネスにも使える。既存のXBRLソフトウェアのバックでこいつが走ってたりもする。開発者は主要なXBRL仕様のEditor、Contributor。GUI版もある。Pythonで開発されている。便利。RESTベースのWebサービスAPIが提供されており、Excel、Java、C＃などに対応。その他色んな機能があるっぽいが、把握できていない。詳しい人からのコメント求む。最新の情報は、[公式サイト](http://arelle.org/)を確認するのがよい。

やや古めだが、資料もこちらに記載しておく。本記事では紹介しないGUIでの利用方法などが書いてある。
http://www.openfiling.info/wp-content/upLoads/data/ArelleUsersManual.pdf
https://studylib.net/doc/10221035/xbrl-arelle-tutorial

※追記：
公式資料は[arelleHP](https://arelle.org/arelle/pub/)からダウンロードできた。ER図や各オブジェクトのメソッドやプロパティは、ComparabilityAndDataMiningUnifiedModel-Paperを参照すると確認できる。実装する際は参照必須
・ComparabilityAndDataMiningUnifiedModel-Paper.pdf
・KU-2013-Evolution-and-Future.pdf
・KU-XBRL-open-source-ArelleProject.pdf
ということで、次の章からArelleを使ってデータ解析をしていく。

# 5.ArelleでXBRLデータ解析


### 環境構築
以下公式サイトからの抜粋。macOS Sierra 10.13.4、Python 3.5.2で構築。その他、Windows7の64bit版でも動作を確認済み。
>The implementation is in Python 3.5, and is intended for Windows (any recent), Mac OS-X 10.9-11, Unix or Linux. 

おもむろに必要なライブラリをインストールする。現時点でarelleのバージョンは2.25.8が最新の模様。
※追記：野良版を紹介していたようだ。正式版はarelle-releaseでOK。PyPIのリンクも修正済み。arelleは[PyPI](https://pypi.org/project/arelle-release/)に登録されている

```
$ pip install lxml
$ pip install isodate
$ pip install arelle-release
```

### データ準備
今回は、EDINETから有価証券報告書のXBRLデータを手動でダウンロードしてきた。
時間がある人は、金融庁や企業で提供しているAPIなどでダウンロードしてみるとよい。
※平成31年3月17日からEDINETに提出された書類をAPIで取得できるようになった。

### プログラム説明
Arelleでインスタンスを処理させて、取得した情報をテキストに吐き出すサンプルプログラムを作成。(プログラムは[7.ソースコード]を参照のこと)
なぜか出力に標準出力を採用。100個近いprint文がコーディングされ、１つのテキストファイルに情報が吐き出されるパンチのあるプログラムが完成した。もう後戻りはできない。本プログラムの利用用途は、Arelleが出力する情報の構造理解、という観点であることは忘れないでほしい。

ここでは、ポイントとなるコードのみ解説する。
また、オブジェクトの関係や構造などは、[設計書っぽいもの](http://arelle.org/wordpress/wp-content/uploads/downloads/2011/09/ComparabilityAndDataMiningUnifiedModel-Paper.pdf)に書いてあるので、適宜参照すると理解が捗る。



#### ポイント1：
`ModelManager`から、インスタンスをロードし、`ModelXbrl`を作成する。
`ModelXbrl`からは、インスタンスが参照している全ドキュメント情報を取得できる。その他取得できる情報は、WhitePaperっぽいものに記載されている`Properties`や、ソース（`ModelXbrl.py`）を見れば分かる。

```Python
modelXbrl =modelManager.load(filePath)
for urlIndex, urlDoc in enumerate(modelXbrl.urlDocs):
    print(" -【ModelXbrl.urlDocs】 :","[",urlIndex+1,"]",urlDoc)
```


<details><summary>出力結果(ModelXbrl)</summary><div>

```
************ModelXbrl************
 -【ModelXbrl.urlDocs】 : [ 1 ] C:\PublicDoc\jpcrp030000-asr-001_E01738-000_2019-03-31_01_2019-06-25.xbrl
 -【ModelXbrl.urlDocs】 : [ 2 ] C:\PublicDoc\jpcrp030000-asr-001_E01738-000_2019-03-31_01_2019-06-25.xsd
 -【ModelXbrl.urlDocs】 : [ 3 ] http://disclosure.edinet-fsa.go.jp/taxonomy/jpcrp/2018-03-31/jpcrp_cor_2018-03-31.xsd
 -【ModelXbrl.urlDocs】 : [ 4 ] http://www.xbrl.org/2003/xbrl-instance-2003-12-31.xsd
 -【ModelXbrl.urlDocs】 : [ 5 ] http://www.xbrl.org/2003/xbrl-linkbase-2003-12-31.xsd
 -【ModelXbrl.urlDocs】 : [ 6 ] http://www.xbrl.org/2003/xl-2003-12-31.xsd
・・・（省略）
```
</div></details>

#### ポイント2：
`modelxbrl.urlDocs`から`ModelDocument`を取得。
`ModelDocument`からは、各ドキュメントのType（インスタンス、スキーマ、リンクベース等）情報や、targetNamespace、名前空間などを取得できる。

```python
for docIndex, urlDoc in enumerate(modelXbrl.urlDocs):
    print("[", docIndex+1, "]", ":ModelDocument")
    print(" -【ModelDocument.type】 :",modelXbrl.urlDocs[urlDoc].type,"[",docType,"]")
    print(" -【ModelDocument.uri】 :",modelXbrl.urlDocs[urlDoc].uri)
    print(" -【ModelDocument.targetNamespace】 :",modelXbrl.urlDocs[urlDoc].targetNamespace)
    print(" -【ModelDocument.referencedNamespaces】 :", modelXbrl.urlDocs[urlDoc].referencedNamespaces)
    print(" +【ModelDocument.xmlRootElement】 :", modelXbrl.urlDocs[urlDoc].xmlRootElement)
    # 【ModelObject Properties】
    print(" - -【ModelObject.localName】 :", modelXbrl.urlDocs[urlDoc].xmlRootElement.localName)
    print(" - -【ModelObject.attrib】 :", modelXbrl.urlDocs[urlDoc].xmlRootElement.attrib)
    print(" - -【ModelObject.defaultNamespaceURI】 :", modelXbrl.urlDocs[urlDoc].xmlRootElement.namespaceURI)
    for i, ns in enumerate(modelXbrl.urlDocs[urlDoc].xmlRootElement.nsmap):
            print(" - -【ModelObject.nsmap.prefix","[", i, "]", ns)
            print(" - -【ModelObject.nsmap.namespaceURI","[", i, "]", modelXbrl.urlDocs[urlDoc].xmlRootElement.nsmap[ns])
```


<details><summary>出力結果(ModelDocument)</summary><div>

```
************ModelDocument************
[ 1 ] :ModelDocument
 -【ModelDocument.type】 : 4 [ instance ]
 -【ModelDocument.uri】 : C:\PublicDoc\jpcrp030000-asr-001_E01738-000_2019-03-31_01_2019-06-25.xbrl
 -【ModelDocument.targetNamespace】 : None
 -【ModelDocument.referencedNamespaces】 : set()
 +【ModelDocument.xmlRootElement】 : ModelObject[1, jpcrp030000-asr-001_E01738-000_2019-03-31_01_2019-06-25.xbrl line 2)
 - -【ModelObject.localName】 : xbrl
 - -【ModelObject.attrib】 : {}
 - -【ModelObject.defaultNamespaceURI】 : http://www.xbrl.org/2003/instance
 - -【ModelObject.nsmap.prefix [ 0 ] link
 - -【ModelObject.nsmap.namespaceURI [ 0 ] http://www.xbrl.org/2003/linkbase
 - -【ModelObject.nsmap.prefix [ 1 ] jpdei_cor
 - -【ModelObject.nsmap.namespaceURI [ 1 ] http://disclosure.edinet-fsa.go.jp/taxonomy/jpdei/2013-08-31/jpdei_cor
 - -【ModelObject.nsmap.prefix [ 2 ] iso4217
 - -【ModelObject.nsmap.namespaceURI [ 2 ] http://www.xbrl.org/2003/iso4217
 - -【ModelObject.nsmap.prefix [ 3 ] xbrldi
 - -【ModelObject.nsmap.namespaceURI [ 3 ] http://xbrl.org/2006/xbrldi
 ・・・（省略）
```
</div></details>

#### ポイント3：
`modelxbrl.facts`から`ModelFact`を取得する。
要は、繰り返し処理でインスタンスに定義されている全factの情報を`MoldelFact`型で取得してくるもの。※構造を考慮すれば、`ModelDocument`にぶら下がる`ModelObject`から`ModelFact`を取得するようだが、今回は簡略化した処理にしている
この`ModelFact`からは、concept、context、unitやvalueなどを取得できる。既にタクソノミの情報と紐づけがされている状態なのはありがたい。各種情報は、いずれも`ModelConcept`、`ModelContext`、`ModelUnit`でオブジェクト化されている。そのため、`ModelFact`の取得に合わせ、これらのオブジェクト情報の詳細も取得し、テキストに吐き出すようにした。以下に1つのfact分のみを抽出し結果として貼り付けている。

```python
print("************ModelFact************")
for i,fact in enumerate(modelXbrl.facts):
        print("[", i+1, "]",":modelFact")

        #【ModelFact Properties】
        print(" +【modelFact.concept】:",fact.concept)

        # 【modelConcept Properties】
        print(" - -【modelConcept.qname】:", fact.concept.qname)
        print(" - -【modelConcept.abstract】:", fact.concept.abstract)
        print(" - -【modelConcept.periodType】:", fact.concept.periodType)
        print(" - -【modelConcept.typeQname】:", fact.concept.typeQname)
        print(" - -【modelConcept.niceType】:", fact.concept.niceType)
        print(" - -【modelConcept.isNumeric】:", fact.concept.isNumeric)
        print(" - -【modelConcept.isFraction】:", fact.concept.isFraction)
        print(" - -【modelConcept.isMonetary】:", fact.concept.isMonetary)
        print(" - -【modelConcept.isShares】:", fact.concept.isShares)
        print(" - -【modelConcept.isTextBlock】:", fact.concept.isTextBlock)
        print(" - -【modelConcept.type】:", fact.concept.type)

        #・・・（省略）
```


<details><summary>出力結果(ModelFact)</summary><div>

```
************ModelFact************

[ 613 ] :modelFact
 +【modelFact.concept】: modelConcept[5049, qname: jppfs_cor:CashAndDeposits, type: xbrli:monetaryItemType, abstract: false, jppfs_cor_2018-03-31.xsd, line 16]
 - -【modelConcept.qname】: jppfs_cor:CashAndDeposits
 - -【modelConcept.abstract】: false
 - -【modelConcept.periodType】: instant
 - -【modelConcept.typeQname】: xbrli:monetaryItemType
 - -【modelConcept.niceType】: Monetary
 - -【modelConcept.isNumeric】: True
 - -【modelConcept.isFraction】: False
 - -【modelConcept.isMonetary】: True
 - -【modelConcept.isShares】: False
 - -【modelConcept.isTextBlock】: False
 - -【modelConcept.type】: modelType[500, qname: xbrli:monetaryItemType, derivedFrom: xbrli:monetary, xbrl-instance-2003-12-31.xsd, line 269]
 - +【modelConcept.substitutionGroup】: modelConcept[729, qname: xbrli:item, type: anyType, abstract: true, xbrl-instance-2003-12-31.xsd, line 739]
 - - -【modelConcept.qname】: xbrli:item
 - - -【modelConcept.abstract】: true
 - - -【modelConcept.niceType】: anyType
 - -【modelConcept.substitutionGroupQnames】: [xbrli:item]
 - -【modelConcept.nillable】: true
 - -【modelConcept.isItem】: True
 - -【modelConcept.isTuple】: False
 - -【modelConcept.isPrimaryItem】: True
 - -【modelConcept.isDomainMember】: True
 - -【modelConcept.isHypercubeItem】: False
 - -【modelConcept.isDimensionItem】: False
 - -【modelConcept.isTypedDimension】: False
 - -【modelConcept.isExplicitDimension】: False
 - -【modelConcept.typedDomainElement】: None
 -【modelFact.contextID】: CurrentYearInstant_NonConsolidatedMember
 +【modelFact.context】: modelContext[CurrentYearInstant_NonConsolidatedMember, period: instant 2019-03-31, dimensions: (1) (('jppfs_cor:ConsolidatedOrNonConsolidatedAxis', 'jppfs_cor:NonConsolidatedMember'),),jpcrp030000-asr-001_E01738-000_2019-03-31_01_2019-06-25.xbrl line 1544]
 - -【modelContext.isStartEndPeriod】: False
 - -【modelContext.isInstantPeriod】: True
 - -【modelContext.isForeverPeriod】: False
 - -【modelContext.startDatetime】: None
 - -【modelContext.endDatetime】: 2019-04-01 00:00:00
 - -【modelContext.instantDatetime】: 2019-04-01 00:00:00
 - +【modelContext.period】: ModelObject[90621, jpcrp030000-asr-001_E01738-000_2019-03-31_01_2019-06-25.xbrl line 1548)
 - - -【ModelObject.elementQname】: xbrli:period
 - +【modelContext.entity】: ModelObject[90620, jpcrp030000-asr-001_E01738-000_2019-03-31_01_2019-06-25.xbrl line 1545)
 - - -【ModelObject.elementQname】: xbrli:entity
 - +【modelContext.entityIdentifierElement】: ModelObject[90623, jpcrp030000-asr-001_E01738-000_2019-03-31_01_2019-06-25.xbrl line 1546)
 - - -【ModelObject.entityIdentifierElement.elementQname】: xbrli:identifier
 - - -【ModelObject.elementAttributesStr】: scheme='http://disclosure.edinet-fsa.go.jp'
 - - -【ModelObject.entityIdentifierElement.textValue】: E01738-000
 - -【modelContext.entityIdentifier】: ('http://disclosure.edinet-fsa.go.jp', 'E01738-000')
 - -【modelContext.segment】: None
 - -【modelContext.scenario】: ModelObject[90622, jpcrp030000-asr-001_E01738-000_2019-03-31_01_2019-06-25.xbrl line 1551)
 - +【modelContext.scenDimValues】: {modelConcept[8920, qname: jppfs_cor:ConsolidatedOrNonConsolidatedAxis, type: xbrli:stringItemType, abstract: true, jppfs_cor_2018-03-31.xsd, line 3887]: ModelDimensionValue[90625, jpcrp030000-asr-001_E01738-000_2019-03-31_01_2019-06-25.xbrl line 1552)}
 - - -【modelConcept.qname】: [ 1 ] jppfs_cor:ConsolidatedOrNonConsolidatedAxis
 - - -【modelConcept.abstract】: [ 1 ] true
 - - -【modelConcept.typeQname】: [ 1 ] xbrli:stringItemType
 - - -【ModelDimensionValue.dimensionQname】: [ 1 ] jppfs_cor:ConsolidatedOrNonConsolidatedAxis
 - - -【ModelDimensionValue.memberQname】: [ 1 ] jppfs_cor:NonConsolidatedMember
 - - -【ModelDimensionValue.contextElement】: [ 1 ] scenario
 -【modelFact.unitID】: JPY
 +【modelFact.unit】: ModelUnit[87974, jpcrp030000-asr-001_E01738-000_2019-03-31_01_2019-06-25.xbrl line 2789)
 - -【ModelUnit.measures】: ((iso4217:JPY,), ())
 - -【ModelUnit.value】: JPY
 +【modelFact.parentElement】: ModelObject[1, jpcrp030000-asr-001_E01738-000_2019-03-31_01_2019-06-25.xbrl line 2)
 - -【ModelObject.elementQname】: xbrli:xbrl
 -【modelFact.ancestorQnames】: {xbrli:xbrl}
 -【modelFact.decimals】: -6
 -【modelFact.precision】: None
 -【modelFact.xmlLang】: None
 -【modelFact.xsiNil】: false
 -【modelFact.value】: 1070861000000
 -【modelFact.fractionValue】: ('', '')
```
</div></details>
<br>

#### ポイント4：
インスタンスの`footnote`を抽出する処理。
`modelXbrl`に含まれる全`modelObjects`を順次処理していく。取得してきた`modelObjects`がfootnote情報などを含む`ModelLink`で、かつ`elementQname`属性が`link:footnoteLink`の場合のみ、footnote情報を取得してくる仕組み。
リンベベース種別を保持する`linkName`を、`link:labelLink`や`link:definitionLink`に一致するかどうかの判定に変更することで、各種リンクベース情報を取得可能。

```python
print("************ModelLink************")
footnoteLinkCount = 1
for modelObject in modelXbrl.modelObjects:
    modelObjectStr = str(modelObject).split('[')[0]
    if modelObjectStr == 'ModelLink':
        linkName = str(modelObject.elementQname)

        if 'link:footnoteLink' == linkName:
        #・・・（省略）
            if 'fact' == name:
            #・・・（省略）

            elif 'footnote' == name:
            #・・・（省略）

```


<details><summary>出力結果(ModelLink)</summary><div>

```
[ 21 ]
 -【ModelLink.elementQname】: link:footnoteLink
 -【ModelLink.elementAttributesTuple】: [ 0 ] ('{http://www.w3.org/1999/xlink}role', 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_StatementOfIncome')
 -【ModelLink.elementAttributesTuple】: [ 1 ] ('{http://www.w3.org/1999/xlink}type', 'extended')
 +【ModelLink.labeledResources】 defaultdict(<class 'list'>, {'footnote': [ModelResource[114368, jpcrp030000-asr-001_E04911-000_2019-03-31_01_2019-06-21.xbrl line 37884)], 'fact': [ModelLocator[114369, jpcrp030000-asr-001_E04911-000_2019-03-31_01_2019-06-21.xbrl line 37885)]})
 - -【ModelLink.fact】 footnote
 - +【ModelLink.value】 [ModelResource[114368, jpcrp030000-asr-001_E04911-000_2019-03-31_01_2019-06-21.xbrl line 37884)]
 - - -【ModelResource.elementQname】: link:footnote
 - - -【ModelResource.xlinkLabel】: footnote
 - - -【ModelResource.role】: http://disclosure.edinet-fsa.go.jp/role/jppfs/role/NotesNumber
 - - -【ModelResource.xmlLang】: ja
 - -【ModelLink.stringValue】: ※１
 - -【ModelLink.fact】 fact
 - +【ModelLink.value】 [ModelLocator[114369, jpcrp030000-asr-001_E04911-000_2019-03-31_01_2019-06-21.xbrl line 37885)]
 - - -【ModelLocator.elementQname】: link:loc
 - - -【ModelLocator.elementAttributesTuple】: [ 0 ] ('{http://www.w3.org/1999/xlink}label', 'fact')
 - - -【ModelLocator.elementAttributesTuple】: [ 1 ] ('{http://www.w3.org/1999/xlink}type', 'locator')
 - - -【ModelLocator.elementAttributesTuple】: [ 2 ] ('{http://www.w3.org/1999/xlink}href', '#fact20')
```
</div></details>
<br>

#### ポイント（全体補足）：
以下表の参照オブジェクトを利用することで、インスタンス情報（footnote含む）を抽出した。仮にinlineXBRLがインプットになる場合は、`ModelFact`以外に、`ModelinlineFact`から情報を取得してくる必要がある。
その他、各種オブジェクトの目的や保持情報のdetailは、公式サイトの[ARCHITECTURE, MODEL](http://arelle.org/documentation/model/) 中段の「The model objects are, from general to more specific:」以降に記載されている。

| ER図| 参照オブジェクト |
|:--------------------------|:------------|
| Model Instance Objects|ModelXbrl、ModelDocument、ModelObject、ModelConcept、ModelFact、ModelContext、ModelUnit、ModelDimensionValue|
| Model DTS Objects|ModelXbrl、ModelDocument、ModelObject、ModelResource、ModelLocator|


![structure_instance.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/472427/1a8ca8d6-bc70-9d36-163c-3bfd1da1502e.png)

![structure_dts.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/472427/5e53ff5b-f91e-e862-6688-4e3097d7831d.png)

# 6.まとめ
　XBRLのインスタンス情報（一部タクソノミ情報有り）を、Arelleを活用することで簡単、かつ仕様通りに解析することができた。
日本国内のXBRLデータのみを解析対象とするならば、システムで開示されているXBRLのアーキテクチャを理解し、解析ソフトウェアを自作することも確かに悪くないのだが、いざ海外のXBRLデータを取り込もうとすると、採用している仕様（TableLinkbase、InlineXBRLv1.1など）が異なっていたり、同じ仕様でもあまり使わない技術(TypedDimension,Tupleなど)を採用しているため解析できないなど、色々問題が発生し始める。
結果として、自作ソフトをカスタマイズすることになる可能性が高い。そしていつの間にかソフトを改修するのに時間が取られ、本来やりたいことに時間が割けなくなっていく。技術を理解するために開発するというのも分かるが、道が険しすぎてやはりオススメできない。
これからXBRLの世界に入るみなさんは、先人の知恵を最大限利用しよう。


# 7.ソースコード

```Python
# -*- coding: utf-8 -*-

from arelle import ModelManager
from arelle import Cntlr
import sys

# 【TODO】出力するテキストパスを記入
fo = open('/Users/XXX/output.txt', 'w', encoding='utf-8')
sys.stdout = fo

# EDINET instance
# 【TODO】処理したいEDINETのインスタンスファイルパスを記入
filePath = '/Users/XXXX/Desktop/jpcrp030000-asr-001_E04911-000_2019-03-31_01_2019-06-21.xbrl'

ctrl = Cntlr.Cntlr()
modelManager = ModelManager.initialize(ctrl)

print("************Cntlr************")
print(" -【Cntlr.userAppDir】 :", ctrl.userAppDir)

print('************WebCache************')
print(" -【WebCache.cacheDir】 :", ctrl.webCache.cacheDir)

print("************ModelManager************")
print(" -【ModelManager.defaultLang】 :", modelManager.defaultLang)

modelXbrl = modelManager.load(filePath)

print("************ModelXbrl************")
for urlIndex, urlDoc in enumerate(modelXbrl.urlDocs):
    print(" -【ModelXbrl.urlDocs】 :", "[", urlIndex + 1, "]", urlDoc)
print(" -【ModelXbrl.langs】 :", modelXbrl.langs)

print("************ModelDocument************")

for docIndex, urlDoc in enumerate(modelXbrl.urlDocs):
    print("[", docIndex + 1, "]", ":ModelDocument")

    docType = ""
    if (modelXbrl.urlDocs[urlDoc].type == 2):
        docType = "schema"
    elif (modelXbrl.urlDocs[urlDoc].type == 3):
        docType = "linkbase"
    elif (modelXbrl.urlDocs[urlDoc].type == 4):
        docType = "instance"
    elif (modelXbrl.urlDocs[urlDoc].type == 5):
        docType = "inlineXBRL"

    print(" -【ModelDocument.type】 :", modelXbrl.urlDocs[urlDoc].type, "[", docType, "]")
    print(" -【ModelDocument.uri】 :", modelXbrl.urlDocs[urlDoc].uri)
    print(" -【ModelDocument.targetNamespace】 :", modelXbrl.urlDocs[urlDoc].targetNamespace)
    print(" -【ModelDocument.referencedNamespaces】 :", modelXbrl.urlDocs[urlDoc].referencedNamespaces)
    print(" +【ModelDocument.xmlRootElement】 :", modelXbrl.urlDocs[urlDoc].xmlRootElement)

    # 【ModelObject Properties】
    print(" - -【ModelObject.localName】 :", modelXbrl.urlDocs[urlDoc].xmlRootElement.localName)
    print(" - -【ModelObject.attrib】 :", modelXbrl.urlDocs[urlDoc].xmlRootElement.attrib)
    print(" - -【ModelObject.defaultNamespaceURI】 :", modelXbrl.urlDocs[urlDoc].xmlRootElement.namespaceURI)

    for i, ns in enumerate(modelXbrl.urlDocs[urlDoc].xmlRootElement.nsmap):
        print(" - -【ModelObject.nsmap.prefix", "[", i, "]", ns)
        print(" - -【ModelObject.nsmap.namespaceURI", "[", i, "]", modelXbrl.urlDocs[urlDoc].xmlRootElement.nsmap[ns])

print("************ModelFact************")
for i, fact in enumerate(modelXbrl.facts):
    print("[", i + 1, "]", ":modelFact")

    # 【ModelFact Properties】
    print(" -【modelFact.id】:", fact.id) #footnote用に追加
    print(" +【modelFact.concept】:", fact.concept)

    # 【modelConcept Properties】
    print(" - -【modelConcept.qname】:", fact.concept.qname)
    print(" - -【modelConcept.abstract】:", fact.concept.abstract)
    print(" - -【modelConcept.periodType】:", fact.concept.periodType)
    print(" - -【modelConcept.typeQname】:", fact.concept.typeQname)
    print(" - -【modelConcept.niceType】:", fact.concept.niceType)
    print(" - -【modelConcept.isNumeric】:", fact.concept.isNumeric)
    print(" - -【modelConcept.isFraction】:", fact.concept.isFraction)
    print(" - -【modelConcept.isMonetary】:", fact.concept.isMonetary)
    print(" - -【modelConcept.isShares】:", fact.concept.isShares)
    print(" - -【modelConcept.isTextBlock】:", fact.concept.isTextBlock)
    print(" - -【modelConcept.type】:", fact.concept.type)

    print(" - +【modelConcept.substitutionGroup】:", fact.concept.substitutionGroup)

    # 【modelConcept Properties】
    print(" - - -【modelConcept.qname】:", fact.concept.substitutionGroup.qname)
    print(" - - -【modelConcept.abstract】:", fact.concept.substitutionGroup.abstract)
    print(" - - -【modelConcept.niceType】:", fact.concept.substitutionGroup.niceType)
    print(" - -【modelConcept.substitutionGroupQnames】:", fact.concept.substitutionGroupQnames)
    print(" - -【modelConcept.nillable】:", fact.concept.nillable)
    print(" - -【modelConcept.isItem】:", fact.concept.isItem)
    print(" - -【modelConcept.isTuple】:", fact.concept.isTuple)
    print(" - -【modelConcept.isPrimaryItem】:", fact.concept.isPrimaryItem)
    print(" - -【modelConcept.isDomainMember】:", fact.concept.isDomainMember)
    print(" - -【modelConcept.isHypercubeItem】:", fact.concept.isHypercubeItem)
    print(" - -【modelConcept.isDimensionItem】:", fact.concept.isDimensionItem)
    print(" - -【modelConcept.isTypedDimension】:", fact.concept.isTypedDimension)
    print(" - -【modelConcept.isExplicitDimension】:", fact.concept.isExplicitDimension)
    print(" - -【modelConcept.typedDomainElement】:", fact.concept.typedDomainElement)

    print(" -【modelFact.contextID】:", fact.contextID)
    print(" +【modelFact.context】:", fact.context)

    # 【modelContext Properties】
    print(" - -【modelContext.isStartEndPeriod】:", fact.context.isStartEndPeriod)
    print(" - -【modelContext.isInstantPeriod】:", fact.context.isInstantPeriod)
    print(" - -【modelContext.isForeverPeriod】:", fact.context.isForeverPeriod)
    print(" - -【modelContext.startDatetime】:", fact.context.startDatetime)
    print(" - -【modelContext.endDatetime】:", fact.context.endDatetime)
    print(" - -【modelContext.instantDatetime】:", fact.context.instantDatetime)
    print(" - +【modelContext.period】:", fact.context.period)

    # 【ModelObject Properties】
    print(" - - -【ModelObject.elementQname】:", fact.context.period.elementQname)

    print(" - +【modelContext.entity】:", fact.context.entity)

    # 【ModelObject Properties】
    print(" - - -【ModelObject.elementQname】:", fact.context.entity.elementQname)

    print(" - +【modelContext.entityIdentifierElement】:", fact.context.entityIdentifierElement)

    # 【ModelObject Properties】
    print(" - - -【ModelObject.entityIdentifierElement.elementQname】:",
          fact.context.entityIdentifierElement.elementQname)
    print(" - - -【ModelObject.elementAttributesStr】:", fact.context.entityIdentifierElement.elementAttributesStr)
    print(" - - -【ModelObject.entityIdentifierElement.textValue】:", fact.context.entityIdentifierElement.textValue)

    print(" - -【modelContext.entityIdentifier】:", fact.context.entityIdentifier)
    print(" - -【modelContext.segment】:", fact.context.segment)
    print(" - -【modelContext.scenario】:", fact.context.scenario)
    print(" - +【modelContext.scenDimValues】:", fact.context.scenDimValues)

    for dimIndex, scenDimValue in enumerate(fact.context.scenDimValues):
        print(" - - -【modelConcept.qname】:", "[", dimIndex + 1, "]", scenDimValue.qname)
        print(" - - -【modelConcept.abstract】:", "[", dimIndex + 1, "]", scenDimValue.abstract)
        print(" - - -【modelConcept.typeQname】:", "[", dimIndex + 1, "]", scenDimValue.typeQname)
        print(" - - -【ModelDimensionValue.dimensionQname】:", "[", dimIndex + 1, "]",
              fact.context.scenDimValues[scenDimValue].dimensionQname)
        print(" - - -【ModelDimensionValue.memberQname】:", "[", dimIndex + 1, "]",
              fact.context.scenDimValues[scenDimValue].memberQname)
        print(" - - -【ModelDimensionValue.contextElement】:", "[", dimIndex + 1, "]",
              fact.context.scenDimValues[scenDimValue].contextElement)

    print(" -【modelFact.unitID】:", fact.unitID)
    print(" +【modelFact.unit】:", fact.unit)

    # 【ModelUnit Properties】
    if fact.unit is not None:  # (empty if not a divide element)
        print(" - -【ModelUnit.measures】:", fact.unit.measures)
        print(" - -【ModelUnit.value】:", fact.unit.value)

    print(" +【modelFact.parentElement】:", fact.parentElement)

    # 【ModelObject Properties】
    print(" - -【ModelObject.elementQname】:", fact.parentElement.elementQname)

    print(" -【modelFact.ancestorQnames】:", fact.ancestorQnames)
    print(" -【modelFact.decimals】:", fact.decimals)
    print(" -【modelFact.precision】:", fact.precision)
    print(" -【modelFact.xmlLang】:", fact.xmlLang)
    print(" -【modelFact.xsiNil】:", fact.xsiNil)
    print(u" -【modelFact.value】:", fact.value)
    print(" -【modelFact.fractionValue】:", fact.fractionValue)

print("************ModelLink(Footnote)************")
footnoteLinkCount = 1
for modelObject in modelXbrl.modelObjects:
    modelObjectStr = str(modelObject).split('[')[0]
    if modelObjectStr == 'ModelLink':
        linkName = str(modelObject.elementQname)

        if 'link:footnoteLink' == linkName:
            print("[", footnoteLinkCount, "]")
            print(" -【ModelLink.elementQname】:", modelObject.elementQname)

            for tupleNum, list in enumerate(modelObject.elementAttributesTuple):
                print(" -【ModelLink.elementAttributesTuple】:", "[", tupleNum, "]", list)

            print(" +【ModelLink.labeledResources】", modelObject.labeledResources)

            for name in modelObject.labeledResources:
                print(" - -【ModelLink.fact】", name)
                print(" - +【ModelLink.value】", modelObject.labeledResources[name])

                if 'fact' == name:
                    for listNum, footnote in enumerate(modelObject.labeledResources[name]):

                        print(
                        " - - -【ModelLocator.elementQname】:", modelObject.labeledResources[name][listNum].elementQname)

                        for tupleNum, list in enumerate(
                                modelObject.labeledResources[name][listNum].elementAttributesTuple):
                            print(" - - -【ModelLocator.elementAttributesTuple】:", "[", tupleNum, "]", list)

                elif 'footnote' == name:
                    for listNum, footnote in enumerate(modelObject.labeledResources[name]):
                        print(" - - -【ModelResource.elementQname】:",
                              modelObject.labeledResources[name][listNum].elementQname)
                        print(" - - -【ModelResource.xlinkLabel】:",
                              modelObject.labeledResources[name][listNum].xlinkLabel)
                        print(" - - -【ModelResource.role】:", modelObject.labeledResources[name][listNum].role)
                        print(
                            " - - -【ModelResource.xmlLang】:", modelObject.labeledResources[name][listNum].xmlLang)
                        print(" - - -【ModelLink.stringValue】:", modelObject.stringValue)

            footnoteLinkCount += 1