2011-01-04 5 views
6

Próbuję do analizowania XML z BeautifulSoup, ale uderzył w mur, gdy próbuje użyć „recursive” atrybutu z findAll()BeautifulSoup zagnieżdżone znaczniki

I wykazały dość dziwne formacie xml poniżej:

<?xml version="1.0"?> 
<catalog> 
    <book> 
     <author>Gambardella, Matthew</author> 
     <title>XML Developer's Guide</title> 
     <genre>Computer</genre> 
     <price>44.95</price> 
     <publish_date>2000-10-01</publish_date> 
     <description>An in-depth look at creating applications 
     with XML.</description> 
     <book>true</book> 
    </book> 
    <book> 
     <author>Ralls, Kim</author> 
     <title>Midnight Rain</title> 
     <genre>Fantasy</genre> 
     <price>5.95</price> 
     <publish_date>2000-12-16</publish_date> 
     <description>A former architect battles corporate zombies, 
     an evil sorceress, and her own childhood to become queen 
     of the world.</description> 
     <book>false</book> 
    </book> 
</catalog> 

Jak widać, znacznik książka powtarza wewnątrz znacznika książka, która powoduje błąd, gdy próbuję coś jak:

from BeautifulSoup import BeautifulStoneSoup as BSS 

catalog = "catalog.xml" 


def open_rss(): 
    f = open(catalog, 'r') 
    return f.read() 

def rss_parser(): 
    rss_contents = open_rss() 
    soup = BSS(rss_contents) 
    items = soup.findAll('book', recursive=False) 

    for item in items: 
     print item.title.string 

rss_parser() 

jak widać na moim soup.findAll Dodałem rekursywną = fałsz, która teoretycznie sprawiłaby, że nie powtórzyłaby się przez znaleziony przedmiot, ale przejdź do następnego.

nie wydają się działać, bo zawsze pojawia się następujący błąd:

File "catalog.py", line 17, in rss_parser 
    print item.title.string 
AttributeError: 'NoneType' object has no attribute 'string' 

Jestem pewien, że robię coś głupiego tutaj, i byłby wdzięczny, gdyby ktoś mógł mi dać jakąś pomoc na jak rozwiązać ten problem.

Zmiana struktury HTML nie jest opcją, ten kod musi działać dobrze, ponieważ może potencjalnie parsować duży plik XML.

Odpowiedz

3

soup.findAll('catalog', recursive=False) zwróci listę zawierającą tylko swoją najwyższego poziomu "katalogu" tag. Ponieważ nie ma "tytułowego" dziecka, item.title to None.

Zamiast tego spróbuj soup.findAll("book") lub soup.find("catalog").findChildren().

Edytuj: OK, problem nie był taki, jak myślałem. Wypróbuj to:

BSS.NESTABLE_TAGS["book"] = [] 
soup = BSS(open("catalog.xml")) 
soup.catalog.findChildren(recursive=False) 
+0

. Błędów dzieci, a robienie tego przez "książkę" zwraca tylko pierwszy element –

+0

Mój błąd, poprawiłem go, aby findChildren(). Ale ".findAll ("book") "działa dla mnie. Czy na pewno użyłeś findAll, nie znajdziesz? –

+0

Moja zła, * I * skończyło się umieszczaniem niewłaściwego XML tutaj. Trochę zaktualizowałem jego strukturę, tak że reprezentuje dokładnie to, co mam, i powód, dla którego soup.findAll ("książka" nie działa dla mnie –

0

BeautifulSoup jest powolny i martwe, zamiast używać lxml :)

>>> from lxml import etree 
>>> rss = open('/tmp/catalog.xml') 
>>> items = etree.parse(rss).xpath('//book/title/text()') 
>>> items 
["XML Developer's Guide", 'Midnight Rain'] 
>>> 
+1

3.2.0 został wydany 6 tygodni temu. Jak to jest martwe? – marcog

+0

@marcog: Sortuj. Była seria 3.1 i 3.0. Rozwój utknął z 3.1. 3.1 miało poważne błędy, ale ludzie nadal go używali, ponieważ * wydawało się, że * jest najnowszą wersją. Tak więc autor zasadniczo ponownie wydał najnowsze wydanie w gałęzi 3.0 w wersji 3.2. Możesz przeczytać więcej tutaj: http://www.crummy.com/2010/11/21/0 – mipadi

+0

W każdym razie są to różne narzędzia. BeautifullSoup chętnie czyta zepsuty kod XML jak w "dzikim" html. W tym przypadku wygląda na to, że można bez problemu używać etree. – jsbueno

2

Wygląda na to, że problem leży w zagnieżdżonych znacznikach book. BautifulSoup ma predefiniowany zestaw tagów, które można zagnieżdżać (BeautifulSoup.NESTABLE_TAGS), ale nie wie, że book może zostać zagnieżdżony, więc przechodzi w winskers.

Customizing the parser, wyjaśnia, co się dzieje i jak można podklasę BeautifulStoneSoup dostosować niestandardowe znaczniki. Oto jak możemy użyć go, aby usunąć problem:

from BeautifulSoup import BeautifulStoneSoup 

class BookSoup(BeautifulStoneSoup): 
    NESTABLE_TAGS = { 
     'book': ['book'] 
    } 

soup = BookSoup(xml) # xml string omitted to keep this short 
for book in soup.find('catalog').findAll('book', recursive=False): 
    print book.title.string 

Jeśli uruchomić to otrzymujemy następujący wynik:

XML Developer's Guide 
Midnight Rain 
+0

Dowolne przykłady, które mógłbyś mi dać? Nie wydaje się, żeby to działało! –

+0

@Marcos Zobacz edytuj – marcog