比利时国家银行 (NBB) 通过其 API 向公众提供上市公司的财务报表。自 2022 年起,这些可以以 JSON 文件形式请求。之前仅在 XBRL 中。
这个问题是关于 XBRL 的。
来源:
据我了解(可能是简化的),XBRL 是常规 XML 文件,此外还能够引用分类法,即为元素内的属性提供“字典”,以便为元素中封装的内容提供语义含义.
示例
<xbrldi:typedMember dimension="dim:afnp"><open:str>John</open:str></xbrldi:typedMember>
鉴于命名空间可用,在某一点或另一点,
dim:afnp
应指向标签first_name
或相等。
我希望能够将此 XBRL 数据转换为 Pandas DataFrame(或 .csv)
由于 API 密钥是必要的,我将提供一段我认为应该足够的匿名代码。该分类法可在我提供的第二个链接中下载 (.zip)。它只是一个包含有组织的文件夹的目录,与命名空间类似,最终会导致其他 xml/xld 文件。
来自 API 的数据如下所示:
<xbrl
xml:lang="nl"
xmlns="http://www.xbrl.org/2003/instance"
xmlns:link="http://www.xbrl.org/2003/linkbase"
xmlns:xbrldi="http://xbrl.org/2006/xbrldi"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:iso4217="http://www.xbrl.org/2003/iso4217"
xmlns:dim="http://www.nbb.be/be/fr/cbso/dict/dim"
>
<!--More namespaces are given but I believe irrelevant here-->
<link:schemaRef xlink:type="simple" xlink:href="http://www.nbb.be/be/fr/cbso/fws/23.0/mod/m87/m87-f.xsd"/>
<!--The file repeats itself with <context> elements, just different id's.-->
<context id="c19">
<entity><identifier scheme="http://www.fgov.be">Some number</identifier></entity>
<period><instant>Some date</instant></period>
<scenario>
<xbrldi:typedMember dimension="dim:afnp"><open:str>John</open:str></xbrldi:typedMember>
<xbrldi:typedMember dimension="dim:annp"><open:str>Doe</open:str></xbrldi:typedMember>
<xbrldi:explicitMember dimension="dim:bas">bas:m31</xbrldi:explicitMember>
<xbrldi:explicitMember dimension="dim:ctc">ctc:m1</xbrldi:explicitMember>
<xbrldi:explicitMember dimension="dim:part">part:m2</xbrldi:explicitMember>
<xbrldi:explicitMember dimension="dim:psn">psn:m12</xbrldi:explicitMember>
</scenario>
</context>
</xbrl>"""
stringIO_xml = StringIO(xml)
我还读到了有关 Brel 的文章,我认为它更关注来自美国的 EDGAR。
但是,我觉得探索 Pandas
pd.read_xml()
或 lxml
并致力于定制脚本来解码财务信息更有趣。
我注意到
pd.read_xml()
提供了参数namespaces
,它接受dict
。由于分类法是静态的,因此只需完成一次到字典的转换。
我想也许提供本地文件夹的链接可能会有所帮助。
我已经尝试过:
pd.read_xml(stringIO_xml, namespaces={'dim':'file:////Users/myname/Downloads/nbb-cbso-23.0.1/www.nbb.be/be/fr/cbso/dict/dim'})
三个
///
用于文件,一个 /
作为我的绝对路径的开头。
但是出现错误:
XMLSyntaxError: Namespace prefix open on str is not defined, line 16, column 51
如果我读到的错误是正确的,那就是
<open:str>John</open:str>
,我什至不知道如何反驳。
pd.read_xml()
是正确的方法还是对于 XBRL 来说太有限了?我读到它可能太有限了,并且 lxml
提供了更多选项(尽管 lxml
是 pd.read_xml()
的解析器。我知道我不应该问广泛的、开放性的问题,所以我会更直接地问。
嗯,处理这些类型的文件确实很复杂。正如您所说,最好使用
lxml
库,与 pandas.read_xml()
相比,它提供了更多的多功能性。
但是得到一个错误:XMLSyntaxError: Namespace prefix open on str is not 定义,第 16 行,第 51 列
根据 API 中数据的最小示例以及您在问题中显示的命名空间,我想说您缺少
open:str
标签的命名空间。这应该可以解决您在 MRE 中遇到的第一个错误:
from lxml import etree
import pandas as pd
from io import StringIO
xml = """
<xbrl
xml:lang="nl"
xmlns="http://www.xbrl.org/2003/instance"
xmlns:link="http://www.xbrl.org/2003/linkbase"
xmlns:xbrldi="http://xbrl.org/2006/xbrldi"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:iso4217="http://www.xbrl.org/2003/iso4217"
xmlns:dim="http://www.nbb.be/be/fr/cbso/dict/dim"
xmlns:open="http://www.example.com/open" ### Example of the missing part
>
<link:schemaRef xlink:type="simple" xlink:href="http://www.nbb.be/be/fr/cbso/fws/23.0/mod/m87/m87-f.xsd"/>
<context id="c19">
<entity><identifier scheme="http://www.fgov.be">Some number</identifier></entity>
<period><instant>Some date</instant></period>
<scenario>
<xbrldi:typedMember dimension="dim:afnp"><open:str>John</open:str></xbrldi:typedMember>
<xbrldi:typedMember dimension="dim:annp"><open:str>Doe</open:str></xbrldi:typedMember>
<xbrldi:explicitMember dimension="dim:bas">bas:m31</xbrldi:explicitMember>
<xbrldi:explicitMember dimension="dim:ctc">ctc:m1</xbrldi:explicitMember>
<xbrldi:explicitMember dimension="dim:part">part:m2</xbrldi:explicitMember>
<xbrldi:explicitMember dimension="dim:psn">psn:m12</xbrldi:explicitMember>
</scenario>
</context>
</xbrl>"""
stringIO_xml = StringIO(xml)
tree = etree.parse(stringIO_xml)
root = tree.getroot()
namespaces = {
'xbrl': 'http://www.xbrl.org/2003/instance',
'xbrldi': 'http://xbrl.org/2006/xbrldi',
'dim': 'http://www.nbb.be/be/fr/cbso/dict/dim',
'open': 'http://www.example.com/open' ### Example of the missing part
}
然后,要访问
XPATH
的元素,您需要像这样进入树结构:
data = []
for context in root.findall('.//xbrl:context', namespaces):
context_id = context.get('id')
entity = context.find('.//xbrl:entity/xbrl:identifier', namespaces).text
period = context.find('.//xbrl:period/xbrl:instant', namespaces).text
scenario = context.find('.//xbrl:scenario', namespaces)
row = {'context_id': context_id, 'entity': entity, 'period': period}
for member in scenario.findall('.//xbrldi:typedMember', namespaces):
dimension = member.get('dimension')
value = member.find('.//open:str', namespaces).text
row[dimension] = value
for member in scenario.findall('.//xbrldi:explicitMember', namespaces):
dimension = member.get('dimension')
value = member.text
row[dimension] = value
data.append(row)
df = pd.DataFrame(data)
再次,我完成了您提供的示例,而不是压缩文件,这可能会导致其他问题,具体取决于 XML 结构的组成方式。不过,我想说,一旦将 XML 加载到字符串中,它应该非常简单。