Вложенные теги BeautifulSoup

Я пытаюсь разобрать XML с Beautifulsoup, но попал в кирпичную стену при попытке использовать "рекурсивные" атрибут с findall ()

у меня довольно странный формат xml, показанный ниже:

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

как вы можете видеть, тег книги повторяется внутри тега книги, что вызывает ошибку, когда я пытаюсь что-то вроде:

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

как вы увидите, на мой суп.findAll я добавил recursive=false, что теоретически не сделало бы его рекурсивным через предмет найден, но переходим к следующему.

Это, похоже, не работает, так как я всегда получаю следующую ошибку:

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

Я уверен, что делаю что-то глупое здесь, и был бы признателен, если бы кто-нибудь мог помочь мне решить эту проблему.

изменение структуры HTML не является опцией, этот код должен хорошо работать, так как он потенциально будет анализировать большой XML-файл.

3 ответов


soup.findAll('catalog', recursive=False) вернет список, содержащий только ваш тег "каталог" верхнего уровня. Поскольку у этого нет "титульного" ребенка,item.title и None.

попробовать soup.findAll("book") или soup.find("catalog").findChildren() вместо.

Edit: хорошо, Проблема была не в том, что я думал. Попробуйте это:

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

похоже, проблема заключается в вложенном book теги. BautifulSoup имеет предопределенный набор тегов, которые могут быть вложены (BeautifulSoup.NESTABLE_TAGS), но он не знает, что book может быть вложенным, поэтому он идет wonkers.

настройки парсера, объясняет, что происходит и как вы можете подкласс BeautifulStoneSoup для настройки вложенных тегов. Вот как мы можем использовать его, чтобы исправить вашу проблему:

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

если мы запустим это, мы получим следующий вывод:

XML Developer's Guide
Midnight Rain

Beautifulsoup медленный и мертвый, вместо этого используйте 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']
>>>