【问题标题】:Parse XML with Python lxml使用 Python lxml 解析 XML
【发布时间】:2021-03-09 01:02:56
【问题描述】:

我正在尝试使用 python 库 lxml 解析 XML,并希望结果输出位于数据框中。我对 python 和解析比较陌生,所以在我概述问题时请多多包涵。我尝试解析的原始 xml 可用 here

我有兴趣在“invstOrSec”中找到一些相关标签。下面是“invstOrSec”的一个实例的快照,其中“curCd”标签附带的文本是美元。

<?xml version="1.0" encoding="UTF-8"?>
    <invstOrSec>
        <name>NIPPON LIFE INSURANCE</name>
        <lei>549300Y0HHMFW3EVWY08</lei>
        <curCd>USD</curCd>
    <invstOrSec>

这个比较简单,我目前的做法是先在字典中定义相关标签,然后在循环中粗化成数据框。

    import pandas as pd
    from lxml import etree

    # Declare directory
    os.chdir('C:/Users/A1610222/Desktop/Form NPORT/pkg/sec-edgar-filings/0001548717/NPORT-P/0001752724- 
    20-040624')

    # Set root
    xmlfile = "filing-details.xml"
    tree = etree.parse(xmlfile)
    root = tree.getroot()

    # Remove namespace prefixes
    for elem in root.getiterator():
        elem.tag = etree.QName(elem).localname
   
    # Remove unused namespace declarations
    etree.cleanup_namespaces(root)

    # Set path
    invstOrSec = root.xpath('//invstOrSec')

    # Define tags to extract
    vars = {'invstOrSec' : {'name', 'lei', 'curCd'}

    # Extract holdings data
    sec_info =  pd.DataFrame()
    temp = pd.DataFrame()

    for one in invstOrSec:
        for two in one:
            if two.tag in vars['invstOrSec']:
                temp[two.tag] = [two.text]
        sec_info = sec_info.append(temp)  

这是 sec_info

的前三行
name lei curCd
NIPPON LIFE INSURANCE 549300Y0HHMFW3EVWY08 USD
Lloyds Banking Group PLC 549300PPXHEU2JF0AM85 USD
Enbridge Inc 98TPTUM4IVMFCZBCUR27 USD

但是,当货币不是美元时,xml 的结构略有不同。请参见下面的示例。

<?xml version="1.0" encoding="UTF-8"?>
    <invstOrSec>
        <name>ACHMEA BV</name>
        <lei>7245007QUMI1FHIQV531</lei>
        <currencyConditional curCd="EUR" exchangeRt="0.89150400"/>
    <invstOrSec>

这一次 curCd 被替换为不同的标签 currencyConditional 并且它包含与文本相反的属性。我很难解释这些情况,同时保持我的代码尽可能通用。我希望我已经设法说明了这个问题。再次,如果这太初级,请原谅我。任何帮助将不胜感激。

【问题讨论】:

    标签: python xml parsing lxml edgar


    【解决方案1】:

    这是您不应该尝试重新发明轮子的一种情况;使用其他人开发的工具...

    import pandas as pd
    import pandas_read_xml as pdx
    
    url = 'https://www.sec.gov/Archives/edgar/data/1548717/000175272420040624/primary_doc.xml'
    
    df = pdx.read_xml(url,['edgarSubmission', 'formData', 'invstOrSecs','invstOrSec'])
    
    #because of the non-US currency column, you have to apply one more contortion:
    df['currencyConditional'] = df['currencyConditional'].apply(lambda x: x.get('@curCd') if not isinstance(x,float) else "NA" )
    df[['name','lei','curCd','currencyConditional']]
    

    输出(显然是部分输出) - 注意最后一行:

    168     BNP PARIBAS     R0MUWSFPU8MPRO8K5P83    USD     NA
    169     Societe Generale    O2RNE8IBXP4R0TD8PU41    USD     NA
    170     BARCLAYS PLC    213800LBQA1Y9L22JB70    NaN     GBP
    

    【讨论】:

    • 非常感谢 Jack 抽出时间回答我的问题。您的解决方案就像一个魅力。我不知道 pandas_read_xml 库。这确实是一个救生员。我有一个快速澄清的问题。您建议的代码中的“if not isinstance(x,float)”有什么意义。我理解语法的作用,但我似乎无法理解为什么我们在这种情况下需要它。再次感谢您的宝贵时间。
    • @stump 有问题的列包含许多NaNs。出于某种原因(我仍然不明白为什么),pandas 将它们视为浮点数。如果您从列表推导中删除 isinstance() 条件,则会引发错误。所以为了跳过它们,有条件是必要的。
    • 这正是我想知道的。奇怪的是 pandas 将 NaN 视为浮点数。谢谢!
    • 在您的帮助下,我已经走了很远,但是我又一次陷入了困境。我正在尝试使用您建议的过程“sec.gov/Archives/edgar/data/1174610/000175272421013053/…”解析以下 xml。如前所述,我有兴趣访问标签“InvstOrSec”下的数据。但是,这一次生成的数据帧输出七个重复行而不是一个唯一行,因为只有一次出现 "invstOrSec" 。我已经成功解析了 1500 多个 xml 文件,这只是一个奇怪的地方。你碰巧有什么线索吗?很抱歉再次打扰您。
    • @stump 有趣的是,您以前没有遇到过这个问题——之前的申报人投资于股票/债务证券;这是投资期货(具体来说是 WTI 原油),这些的基本概念有些不同。要真正进入这个评论太长了,所以你最好将它作为一个单独的问题发布。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-22
    • 1970-01-01
    相关资源
    最近更新 更多