HTML和BeautifulSoup:如何在事先不知道结构的情况下迭代解析?

时间:2014-03-26 20:26:05

标签: python html json parsing beautifulsoup

我从一个简单的HTML结构开始,如下所示:

enter image description here

感谢@alecxe的帮助,我能够创建这个JSON词典:

{u'Outer List': {u'Inner List': [u'info 1', u'info 2', u'info 3']}}

使用他的代码:

from bs4 import BeautifulSoup

data = """your html goes here: see the very end of post""" 
soup = BeautifulSoup(data)

inner_ul = soup.find('ul', class_='innerUl')
inner_items = [li.text.strip() for li in inner_ul.ul.find_all('li')]

outer_ul_text = soup.ul.span.text.strip()
inner_ul_text = inner_ul.span.text.strip()

result = {outer_ul_text: {inner_ul_text: inner_items}}
print result

代码太棒了,我一直试图以可迭代的方式重写它。

我的真实' HTML数据集更大,更糟糕,我需要以我能处理的方式扩展代码:

enter image description here

或者,数据看起来可能是这样的:

enter image description here

更糟糕的是,或许在sublist之下我们还有另一个sublist!最终,这是我的真实情况。

我的问题是:我无法找到一种方法来概括上述的BeautifulSoup代码来处理上述任何一种情况(更不用说第三种情况了!甚至更糟糕的情况!)。

当我无法预先访问HTML的确切结构时,如何递归/迭代地检测HTML的深度并提取信息? BeautifulSoup甚至可以实现这一点吗?当然必须有一些方法可以让我失踪,先确定深度,然后继续。

非常感谢你做到这一点!

最后一个示例的HTML在这里:

<html>
 <body>
  <ul class="rootList">
   <li class="liItem endPlus">
    <span class="itemToBeAdded">
     Outer List
    </span>
   </li>
   <li class="noBulletsLi ">
    <ul class="innerUl">
     <li class="liItem crossPlus">
      <span class="itemToBeAdded">
       Inner List
      </span>
      <ul class="grayStarUl ">
       <li class="">
        <span class="phrasesToBeAdded">
         info 1
        </span>
       </li>
       <li class="">
        <span class="phrasesToBeAdded">
         info 2
         </span>
       </li>
       <li class="">
        <span class="phrasesToBeAdded">
         info 3
        </span>
             <ul class="grayStarUl">
                 <li class="">
                     <span class="phrasesToBeAdded">sublist</span>
                 </li>
             </ul>            
       </li>
      </ul>
     </li>
      </ul>
     </li>
    </ul>
 </body>
</html>

2 个答案:

答案 0 :(得分:3)

你可以编写两个递归调用的解析器:

def parse_list(tag):
    return map(parse_list_item, tag.find_all('li', recursive=False))

def parse_list_item(tag):
    text = tag.find(text=True, recursive=False).strip()
    text += '\n' + tag.span.text.strip() if tag.span.parent == tag else ''
    inner = tag.find('ul', recursive=False)
    if inner is None:  # no more nesting:
        return text.strip()
    else:  # more nesting
        return {text.strip():parse_list(inner)} if text else parse_list(inner)

上面没有使用任何class信息,无论内部列表的深度如何都应该有效:

>>> parse_list(soup.find('ul'))
[u'Outer List', [{u'Inner List': [u'info 1', u'info 2', {u'info 3': [u'sublist']}]}]]

答案 1 :(得分:0)

我对你想要达到的目标有点不确定。因此,我假设您想从所有跨度中提取数据,而不关心结构。如果你更准确地解释你想要实现的目标,我会更新我的答案。

soup = BeautifulSoup(html_doc)
spans = soup.findall(class="phrasesToBeAdded")
text = []
for element in spans:
    text.append(element.get_text())