2011-01-04 16 views
6

Estoy tratando de analizar un XML con BeautifulSoup, pero frente a un muro cuando se trata de utilizar el "recursive" atributo con findall()BeautifulSoup etiquetas anidadas

Tengo un formato XML bastante extraño se muestra a continuación:

<?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> 

como se puede ver, la etiqueta libro repite dentro de la etiqueta del libro, lo que provoca un error cuando intento a algo como:

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() 

como se verá, en mi soup.findTodos he añadido recursive = false, que en teoría no haría recurse a través del elemento encontrado, pero salta al siguiente.

no parece que esto funcione, ya que siempre sale el siguiente error:

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

Estoy seguro de que estoy haciendo algo estúpido aquí, y agradecería si alguien me podría dar un poco de ayuda en Cómo resolver este problema.

Cambiar la estructura HTML no es una opción, este código debe tener un buen rendimiento, ya que potencialmente analizará un archivo XML de gran tamaño.

Respuesta

3

soup.findAll('catalog', recursive=False) devolverá una lista que contiene sólo el código de nivel superior "catálogo". Como no tiene un hijo "title", item.title es None.

Pruebe soup.findAll("book") o soup.find("catalog").findChildren() en su lugar.

Edit: Bien, el problema no era lo que yo pensaba que era. Prueba esto:

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

.children errors, y hacerlo por "libro" solo devuelve el primer elemento –

+0

Mi error, lo he corregido para findChildren(). Pero '.findAll ("book") 'funciona para mí. ¿Estás seguro de que usaste FindAll, not find? –

+0

Malo, * I * terminó publicando el XML incorrecto aquí. He actualizado un poco la estructura, así que representa exactamente lo que tengo, y la razón por la cual soup.findAll ("book" no funciona para mí –

0

BeautifulSoup es lento y muertos, utilice lxml lugar :)

>>> 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 fue lanzado hace 6 semanas. ¿Cómo está eso muerto? – marcog

+0

@marcog: más o menos. Hubo una serie 3.1 y una serie 3.0. Desarrollo estancado con 3.1. 3.1 tenía errores graves, pero la gente siguió usándolo porque * parecía * ser el último lanzamiento. Así que el autor básicamente relanzó lo último en la rama 3.0 como 3.2. Puede leer más aquí: http://www.crummy.com/2010/11/21/0 – mipadi

+0

De todos modos, estas son herramientas diferentes. BeautifullSoup es genial para leer XML rotos como en html "salvaje". En este caso, parece que etree podría usarse sin ningún problema. – jsbueno

2

Parece que el problema radica en los anidados book etiquetas. BautifulSoup tiene un conjunto predefinido de etiquetas que se pueden anidar (BeautifulSoup.NESTABLE_TAGS), pero no sabe que book se puede anidar, por lo que va bien.

Customizing the parser, explica lo que está sucediendo y cómo puede crear la subclase BeautifulStoneSoup para personalizar las etiquetas anidables. Así es como podemos usarlo para arreglar el problema:

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 

Si corremos esto, obtenemos el siguiente resultado:

XML Developer's Guide 
Midnight Rain 
+0

¿Algún tipo de ejemplo que pueda darme? ¡Parece que no funciona! –

+0

@Marcos Ver edit – marcog

Cuestiones relacionadas