【问题标题】:How to use lxml to grab specific parts of an XML document?如何使用 lxml 获取 XML 文档的特定部分?
【发布时间】:2011-05-26 06:42:35
【问题描述】:

我正在使用 Amazon 的 API 来接收有关图书的信息。我正在尝试使用 lxml 来提取我的应用程序所需的 XML 文档的特定部分。 不过,我不确定如何使用 lxml。 这是据我所知:

root = etree.XML(response)

为 XML 文档创建一个 etree 对象。

XML 文档如下所示: http://pastebin.com/GziDkf1a 实际上有多个“项目”,但我只粘贴其中一个给你一个具体的例子。 对于每个项目,我想提取标题和 ISBN。我如何使用我拥有的 etree 对象来做到这一点?

<ItemSearchResponse><Items><Item><ItemAttributes><Title>I want this info</Title></ItemAttributes></Item></Items></ItemSearchResponse

<ItemSearchResponse><Items><Item><ItemAttributes><ISBN>And I want this info</ISBN></ItemAttributes></Item></Items></ItemSearchResponse

基本上,我不知道如何使用我的 etree 对象遍历树,我想学习如何。

编辑 1: 我正在尝试以下代码:

tree = etree.fromstring(response)
for item in tree.iterfind(".//"+AMAZON_NS+"ItemAttributes"):
    print(item)
    print(item.items()) # Apparently, there is nothing in item.items()
    for key, value in item.items():
        print(key)
        print(value)

但我得到以下输出: http://dpaste.com/287496/

我添加了 print(item.items()),它似乎只是一个空列表。虽然每个项目都是一个元素,但由于某种原因,它们没有项目。

编辑 2: 我可以使用下面的代码来获取我想要的信息,但是看起来lxml必须有更简单的方法......(这种方式看起来效率不高):

for item in tree.iterfind(".//"+AMAZON_NS+"ItemAttributes"):
    title_text = ""
    author_text = ""
    isbn_text = ""
    for isbn in item.iterfind(".//"+AMAZON_NS+"ISBN"):
        isbn_text = isbn.text
    for title in item.iterfind(".//"+AMAZON_NS+"Title"):
        title_text = title.text
    for author in item.iterfind(".//"+AMAZON_NS+"Author"):
        author_text = author.text
    print(title_text + " by " + author_text + " has ISBN: " + isbn_text)

【问题讨论】:

  • 您好,您好,您接受的答案无效。请参阅我对该答案的评论。请注意,我提供了一个 tested working 答案。

标签: python lxml


【解决方案1】:

经过测试,它可以与运行 Python 2.7.1 的 lxml.etree 和 xml.etree.cElementTree 一起使用。

import lxml.etree as ET
# Also works with cElementTree (included in recent standard CPythons).
# Use this import:
# import xml.etree.cElementTree as ET
t = ET.fromstring(xmlstring) # your data -- with 2 missing tags added at the end :-)
AMAZON_NS = "{http://webservices.amazon.com/AWSECommerceService/2009-10-01}"
# Find all ItemAttributes elements.
for ia in t.iter(AMAZON_NS+'ItemAttributes'):
    # An ItemAttributes element has *children* named ISBN, Title, Author, etc.
    # NOTE WELL: *children* not *attributes*
    for tag in ('ISBN', 'Title'):
        # Find the first child with that name ...
        elem = ia.find(AMAZON_NS+tag)
        print "%s: %r" % (tag, elem.text)

输出:

ISBN: '0534950973'
Title: 'Introduction to the Theory of Computation'

如果你想生成一个包含ItemAttributes 节点所有子节点的字典,只需要一个小的变化:

import lxml.etree as ET
# Also works with cElementTree (included in recent standard CPythons).
# Use this import:
# import xml.etree.cElementTree as ET
from pprint import pprint as pp
t = ET.fromstring(xmlstring)
AMAZON_NS = "{http://webservices.amazon.com/AWSECommerceService/2009-10-01}"
TAGPOS = len(AMAZON_NS)
# Find all ItemAttributes elements.
for ia in t.iter(AMAZON_NS+'ItemAttributes'):
    item = {}
    # Iterate over all the children of the ItemAttributes node
    for elem in ia:
        # remove namespace stuff from key, remove extraneous whitepace from value
        item[elem.tag[TAGPOS:]] = elem.text.strip()
    pp(item)

输出是:

{'Author': 'Michael Sipser',
 'Binding': 'Hardcover',
 'DeweyDecimalNumber': '511.35',
 'EAN': '9780534950972',
 'Edition': '2',
 'ISBN': '0534950973',
 'IsEligibleForTradeIn': '1',
 'Label': 'Course Technology',
 'Languages': '',
 'ListPrice': '',
 'Manufacturer': 'Course Technology',
 'NumberOfItems': '1',
 'NumberOfPages': '400',
 'PackageDimensions': '',
 'ProductGroup': 'Book',
 'ProductTypeName': 'ABIS_BOOK',
 'PublicationDate': '2005-02-15',
 'Publisher': 'Course Technology',
 'Studio': 'Course Technology',
 'Title': 'Introduction to the Theory of Computation',
 'TradeInValue': ''}

【讨论】:

    【解决方案2】:

    由于您将整个响应作为一个大型 XML 字符串获取,因此您可以使用 lxml 的“fromstring”方法将其转换为完整的 ElementTree 对象。然后,您可以使用 findall 函数(或者实际上,因为您想遍历结果,所以使用 iterfind 函数),但有一个问题:亚马逊的 XML 响应是命名空间的,因此您必须考虑到这一点才能使用 lxml 库正确搜索它。这样的事情应该可以解决问题:

    root=etree.fromstring(responseFromAmazon)
    
    # this creates a constant with the namespace in the form that lxml can use it
    AMAZON_NS="{http://webservices.amazon.com/AWSECommerceService/2009-10-01}"
    
    # this searches the tree and iterates over results, taking the namespace into account
    for eachitem in root.iterfind(".//"+AMAZON_NS+"ItemAttributes"):
       for key,value in eachitem.items():
            if key == 'ISBN':
                  # Do your stuff
            if key == 'Title':
                  # Do your stuff
    

    编辑 1

    看看这是否效果更好:

    root=etree.fromstring(responseFromAmazon)
    AMAZON_NS="{http://webservices.amazon.com/AWSECommerceService/2009-10-01}"
    item={}    
    for attr in root.iterfind(".//"+AMAZON_NS+"ItemAttributes"):
         item[attr[0].tag.replace(AMAZON_NS,"")]=attr[0].text
    

    然后,您可以根据需要访问 item["Title"]、item["ISBN"] 等。

    【讨论】:

    • 您好,由于某种原因,for 循环中的每个“每个项目”仅在我对其运行 .items() 时返回一个空列表。我已经编辑了主要帖子以表明这一点。新内容位于文本“Edit 1:”下。
    • 我的错误......我对 Amazon WS 架构的研究不够好,所以我的示例还没有完全完成。当您执行初始“iterfind”函数调用时,每个结果都可以直接访问子元素,而无需再次迭代。也许一种有效的方式来处理你正在尝试的事情可以像我在上面的编辑 1 中那样使用。
    • FAIL ...在该代码的末尾,print(item) 产生{'Author': 'Michael Sipser'} ...它仅获取 iterfind 找到的元素的第一个子元素。
    • 我假设有多个 元素并且每个元素都有一个子元素(假设这是在初始帖子的代码框中输入的内容)。 John Machin 的解决方案更正确地处理粘贴到 pastebin 链接中的 XML 文本,因为它确实处理了 的多个子项。
    【解决方案3】:

    我建议先使用pyaws。这样您就不必担心 XML 解析。如果没有,您可以使用以下效果:

    from lxml import etree
    
    tree = etree.parse(xmlResponse)
    tree.xpath('//ISBN')[0].text
    

    【讨论】:

    • 嗨,我实际上必须使用多个 API,所以我试图获得一种通用的方法来处理一个网站的 API(在本例中为亚马逊),以便我可以将其应用于其他网站。我会尝试你的方法并报告!
    【解决方案4】:
    from lxml import etree
    root = etree.XML("YourXMLData")  
    items = root.findall('.//ItemAttributes')
    for eachitem in items:
        for key,value in eachitem.items():
            if key == 'ISBN':
                  # Do your stuff
            if key == 'Title':
                  # Do your stuff
    

    这是一种方法。您可以使用它,而不是将 XML 作为字符串加载,您可以使用 parse 方法。但他们的关键是使用find 方法及其朋友转到您的特定节点,然后遍历节点字典。

    【讨论】:

    • 嗨,当我执行 items = root.findall('.//ItemAttributes') 时,我只会返回一个空的项目列表。
    • 而不是 findall,做一个 find 看看你得到了什么。此外,在其父节点上进行查找并查看结果。根据您的 XML,您应该能够到达确切的节点。甚至我不得不在节点周围玩几次。 lxml教程有一些关于查找的信息。
    • 嗯,如果我搜索任何节点标签,它们都会显示为无:dpaste.com/287206
    • 我看到你得到一个元素对象,你应该有一个元素树对象才能使用查找。 root = etree.parse(some_file_like)
    • 您好,我使用 etree.ElementTree() 将我的元素对象转换为元素树,但我仍然做不到:dpaste.com/287238
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-15
    • 1970-01-01
    • 2022-10-17
    • 1970-01-01
    • 1970-01-01
    • 2020-06-19
    • 2020-07-10
    相关资源
    最近更新 更多