问题描述
我正在尝试直接从 SEC 解析财务信息,并且有一个关于在 excel 中使用 VBA 解析 XML 文档的问题。我知道可以通过引用子节点和项目编号的文档进行处理,但该文档非常庞大,需要很长时间才能通读并识别我需要的每个项目。
我在网上看到使用 XPATH 来更有效地查询 XML 文档是很常见的。我尝试了很多方法,但到目前为止还没有成功。我相信我的问题是了解元素所在的命名空间以及如何正确引用指定命名空间下的元素。
下面是我尝试引用任意元素的代码的一部分,
Sub SecData()
Dim xml_obj As MSXML2.XMLHTTP60
Dim xDoc As New MSXML2.DOMDocument60
Dim xml_url As String
Dim nodes As Variant
Set xml_obj = New MSXML2.XMLHTTP60
xml_url = "https://www.sec.gov/Archives/edgar/data/320193/000032019321000010/aapl-20201226_htm.xml"
xml_obj.Open bstrMethod:="GET",bstrURL:=xml_url
xml_obj.send
xDoc.LoadXML (xml_obj.responseText)
xDoc.SetProperty "SelectionLanguage","XPath"
xDoc.SetProperty "SelectionNamespaces","xmlns:link='http://www.xbrl.org/2003/linkbase'"
nodes = xDoc.SelectNodes("//RevenueFromContractWithCustomerExcludingAssessedTax")
XML 文档太大,无法包含在问题中,所以我将在下面留下链接,
https://www.sec.gov/Archives/edgar/data/320193/000032019321000010/aapl-20201226_htm.xml
任何帮助将不胜感激!
谢谢
解决方法
XML: do child nodes inherit parent's namespace prefix? 涵盖命名空间继承。
如果命名空间没有这样的前缀:
xmlns="http://www.xbrl.org/2003/instance"
然后它被它下面的任何东西继承。
如果有前缀(这里是“xbrldi”)
xmlns:xbrldi="http://xbrl.org/2006/xbrldi"
那么它只有在元素中显式使用时才会被继承,例如:
<xbrldi:explicitMember dimension="us-gaap:StatementClassOfStockAxis">
您查询中的元素将“us-gaap”作为命名空间别名,因此您需要将其添加到命名空间集合中,并在您的 xpath 中包含该别名:
us-gaap:RevenueFromContractWithCustomerExcludingAssessedTax
例如:
Dim xDoc As New MSXML2.DOMDocument60
Dim nodes As Variant
xDoc.resolveExternals = False 'don't try to resolve external resources
xDoc.validateOnParse = True
xDoc.Load "C:\Temp\aapl-20201226_htm.xml" 'using local copy for testing...
Debug.Print xDoc.parseError.reason 'in case of problems
xDoc.SetProperty "SelectionLanguage","XPath"
'add namespaces: the first one is the default namespace with a "dummy" prefix of "xxx"
xDoc.SetProperty "SelectionNamespaces",_
"xmlns:xxx='http://www.xbrl.org/2003/instance' " & _
"xmlns:link='http://www.xbrl.org/2003/linkbase' " & _
"xmlns:us-gaap='http://fasb.org/us-gaap/2020-01-31'"
'+ other namespaces as needed...
'element with no prefix: using the "dummy" `xxx` prefix we added for the default namespace
Set nodes = xDoc.SelectNodes("//xxx:context")
Debug.Print nodes.Length ' 207
'these elements belong to a specific namespace so use that prefix...
Set nodes = xDoc.SelectNodes("//us-gaap:RevenueFromContractWithCustomerExcludingAssessedTax")
Debug.Print nodes.Length ' 28