使用BeautifulSoup来寻找所有 "ul "和 "li "元素,并有微小的偏差。

问题描述 投票:0回答:1

在python的一个爬虫脚本中,我使用BeautifulSoup来解析一个html多列表,几乎与问题的含义相同{。使用BeautifulSoup寻找所有 "ul "和 "li "元素。},并由Martijn Pieters使用他的python函数parse_ul()回答。

def parse_ul(elem):
result = {}
for sub in elem.find_all('li', recursive=False):
    if sub.a is None:
        continue
    data = {k: v for k, v in sub.a.attrs.items() if k != 'class'}
    if sub.ul is not None:
        # recurse down
        data['children'] = parse_ul(sub.ul)
    result[sub.a.get_text(strip=True)] = data
return result      

我需要解析一个不符合Martijn Pieters解析器规则的多列。这个html多列包含一个双 <ul></ul> 在单个 <li> .. </li> 其中最后一节得到了一个 <a ... > text </a> 前缀 <li>)

例如 <li><a ...></a><ul> </ul> <a..></a><ul> </ul> </li>

见下文

<ul>
  <li><a class="ref" href="#ref1">Data1</a></li>
  <li><a class="ref" href="#ref2">Data2</a>
    <ul>
      <li><a class="ref" href="#ref4">Data4</a>
        <ul>
          <li><a class="ref" href="#ref5"><span class="pre">Data5</span></a>/li>
          <li><a class="ref" href="#ref6"><span class="pre">Data6</span></a></li>
           .
           .
        </ul>
   <!-- a-tag without preceding <li> tag  -->
        <a class="ref" href="#ref4a">Data4a</a>
        <ul>
          <li><a class="ref" href="#ref5a"><span class="pre">Data5a</span></a></li>
          <li><a class="ref" href="#ref6a"><span class="pre">Data6a</span></a></li>
           .
           .
        </ul>               
      </li>
    </ul>
  </li>
   .
   .
</ul>

我不知道如何改变parse_ul(),使它接受这个偏差并输出这个?Martijn Pieters的解析器是辉煌的,一定也有一个辉煌的解决方案,我的问题:-)

{'Data1': {'href': '#ref1'},
 'Data2': {'children': {'Data4': {'children': {'Data5': {'href': '#ref5'},
                                               'Data6': {'href': '#ref6'}}},
                                 'href': '#ref4'},
                       {'Data4a': {'children':{'Data5a': {'href': '#ref5a'},
                                               'Data6a': {'href': '#ref6a'}}},
                                 'href': '#ref4a'},
           'href': '#ref2'}
}    

下面的脚本。

from bs4 import BeautifulSoup
import pprint

pp = pprint.PrettyPrinter(indent=4)     # Init pritty print (pprint)
soup = BeautifulSoup(html_contents, 'lxml')
menu_dict = parse_ul(soup.ul)
pp.pprint(menu_dict)    

将产生以下输出,其中缺少了第二部分包含在 <a..></a><ul> </ul>:

{'Data1': {'href': '#ref1'},
 'Data2': {'children': {'Data4': {'children': {'Data5': {'href': '#ref5'},
                                               'Data6': {'href': '#ref6'}}},
                                 'href': '#ref4'},
           'href': '#ref2'}
}    
python html list beautifulsoup html-parsing
1个回答
0
投票

一个可能的解决方法是在解析前修改HTML代码,这样可以找到所有的 "ul "和 "li "元素。

这样就可以找到所有的 "ul "和 "li "元素 <a> 标签与以前的兄弟姐妹 <ul> 并将其插入到适当的位置。

txt = '''<ul>
  <li><a class="ref" href="#ref1">Data1</a></li>
  <li><a class="ref" href="#ref2">Data2</a>
    <ul>
      <li><a class="ref" href="#ref4">Data4</a>
        <ul>
          <li><a class="ref" href="#ref5"><span class="pre">Data5</span></a>/li>
          <li><a class="ref" href="#ref6"><span class="pre">Data6</span></a></li>
        </ul>

        <!-- a-tag without preceding <li> tag  -->
        <a class="ref" href="#ref4a">Data4a</a>
        <ul>
          <li><a class="ref" href="#ref5a"><span class="pre">Data5a</span></a></li>
          <li><a class="ref" href="#ref6a"><span class="pre">Data6a</span></a></li>
        </ul>
      </li>
    </ul>
  </li>
</ul>'''

from bs4 import BeautifulSoup

soup = BeautifulSoup(txt, 'html.parser')

# find "lone" <A>+<UL> tags, wrap them in new <LI> tag and append the li tag after parent <LI>:
for a in soup.select('a'):
    prev = a.find_previous_sibling()
    if prev and prev.name == 'ul':
        parent_li = a.parent
        next_ul = a.find_next('ul')

        new_li = soup.new_tag("li")
        new_li.append(a)
        new_li.append(next_ul)

        parent_li.insert_after(new_li)


# this is unchanged code from your question:
def parse_ul(elem):
    result = {}
    for sub in elem.find_all('li', recursive=False):
        if sub.a is None:
            continue
        data = {k: v for k, v in sub.a.attrs.items() if k != 'class'}
        if sub.ul is not None:
            # recurse down
            data['children'] = parse_ul(sub.ul)
        result[sub.a.get_text(strip=True)] = data
    return result

from pprint import pprint
pprint(parse_ul(soup.ul))

打印:

{'Data1': {'href': '#ref1'},
 'Data2': {'children': {'Data4': {'children': {'Data5': {'href': '#ref5'}},
                                  'href': '#ref4'},
                        'Data4a': {'children': {'Data5a': {'href': '#ref5a'},
                                                'Data6a': {'href': '#ref6a'}},
                                   'href': '#ref4a'}},
           'href': '#ref2'}}
© www.soinside.com 2019 - 2024. All rights reserved.