Как использовать lxml для захвата определенных частей документа XML? - PullRequest
1 голос
/ 16 декабря 2010

Я использую API Amazon для получения информации о книгах.Я пытаюсь использовать lxml для извлечения определенных частей документа XMl, которые необходимы для моего приложения.Я не совсем уверен, как использовать lxml, хотя.Это насколько я получил:

root = etree.XML(response)

Чтобы создать объект etree для XML-документа.

Вот как выглядит 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/

Я добавил печать (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)

Ответы [ 4 ]

2 голосов
/ 17 декабря 2010

Это проверено на работу как с lxml.etree, так и с xml.etree.cElementTree, работающими на Python 2.7.1.

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': ''}
1 голос
/ 16 декабря 2010
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 и его друзей, чтобы перейти к вашему конкретному узлу, а затем перебрать словарь узлов.

1 голос
/ 16 декабря 2010

Поскольку вы получаете весь ответ в виде одной большой XML-строки, вы можете использовать метод lxml 'fromstring', чтобы получить его в полном объекте ElementTree.Затем вы можете использовать функцию findall (или фактически, так как вы хотите перебрать результаты, функцию iterfind), но есть одна загвоздка: XML-ответы Amazon имеют пространство имен, поэтому вы должны учитывать это для библиотек 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

Тогда выпри необходимости можете получить доступ к элементу ["Заголовок"], элементу ["ISBN"] и т. д.

1 голос
/ 16 декабря 2010

Я бы рекомендовал сначала использовать pyaws . Тогда вам не придется беспокоиться о разборе XML. Если нет, вы можете использовать что-то с эффектом:

from lxml import etree

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