Python 使用BeautifulSoup以查找所有;ul";及;李",;元素

Python 使用BeautifulSoup以查找所有;ul";及;李",;元素,python,beautifulsoup,html-lists,Python,Beautifulsoup,Html Lists,我目前正在用Python编写一个爬行脚本,我想将下面的HTML响应映射到一个多列表或字典中(这无关紧要) 我目前的代码是: from bs4 import BeautifulSoup from urllib.request import Request, urlopen req = Request("https://my.site.com/crawl", headers={'User-Agent': 'Mozilla/5.0'}) webpage = urlopen(req) soup

我目前正在用Python编写一个爬行脚本,我想将下面的HTML响应映射到一个多列表或字典中(这无关紧要)

我目前的代码是:

from bs4 import BeautifulSoup
from urllib.request import Request, urlopen

req     = Request("https://my.site.com/crawl", headers={'User-Agent': 'Mozilla/5.0'})
webpage = urlopen(req)
soup    = BeautifulSoup(webpage, 'html.parser')
ul      = soup.find('ul', {'class': ''})
运行此命令后,我得到存储在ul中的以下结果:

<ul>
    <li><a class="reference" href="#ref1">Data1</a></li>
    <li><a class="reference" href="#ref2">Data2</a>
        <ul>
            <li><a class="reference" href="#ref3">Data3</a></li>
            <li><a class="reference" href="#ref4">Data4</a>
                <ul>
                    <li><a class="reference" href="#ref5"><span class="pre">Data5</span></a></li>
                    <li><a class="reference" href="#ref6"><span class="pre">Data6</span></a></li>
                    .
                    .
                    .
                </ul>
            </li>
        </ul>
    </li>
    <li><a class="reference" href="#ref7">Data7</a>
        <ul>
            <li><a class="reference" href="#ref8"><span class="pre">Data8</span></a></li>
            <li><a class="reference" href="#ref9"><span class="pre">Data9</span></a></li>
            .
            .
            .
        </ul>
    </li>
    <li><a class="reference" href="#ref10">Data10</a>
        <ul>
            <li><a class="reference" href="#ref11"><span class="pre">Data11</span></a></li>
            <li><a class="reference" href="#ref12">Data12</a></li>
        </ul>
    </li>
</ul>
我确实觉得这是一个麻烦的过程,但是我看不到任何其他的方法

非常感谢任何能让我走上正确方向的帮助


干杯

只需递归
ul
元素,拉出所有包含文本的
li
元素的文本,如果存在
元素,则递归更深:

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
这需要所有直接
li
元素;如果存在
元素,则该锚元素的文本将转换为键,我们将标记属性的副本存储为值(忽略任何
属性)。如果
标记旁边还有
元素,则递归解析该元素,并将其作为
标记的属性字典的
子项添加

对于示例输入,这将生成:

>>> from pprint import pprint    
>>> pprint(parse_ul(soup.ul))
{'Data1': {'href': '#ref1'},
 'Data10': {'children': {'Data11': {'href': '#ref11'},
                         'Data12': {'href': '#ref12'}},
            'href': '#ref10'},
 'Data2': {'children': {'Data3': {'href': '#ref3'},
                        'Data4': {'children': {'Data5': {'href': '#ref5'},
                                               'Data6': {'href': '#ref6'}},
                                  'href': '#ref4'}},
           'href': '#ref2'},
 'Data7': {'children': {'Data8': {'href': '#ref8'}, 'Data9': {'href': '#ref9'}},
           'href': '#ref7'}}
没有简单的方法可以做到这一点,但也没有那么麻烦

例如,您可以递归地执行此操作,如下所示:

def make_data(ul):
    d = {}
    for a in ul.find_all('a'):
        d[a.text] = {'href': a.attrs['href']}
    lis = ul.find_all('li', recursive=False)
    children = {}
    for li in lis:
        child = li.ul
        if child:
            children[li.a.attrs['href']] = make_data(child)
    if children:
        d['children'] = children
    return d
(我必须给每个
孩子
口述一个键,因为你真正想要的结构不是有效的口述。)


当然,你会想,例如,添加一些错误处理,但这应该足够让你开始了。

我非常喜欢Martijn Pieters parse_ul(),但我有一些代码没有遵循这个解析器的规则,在一个
  • 中有一个双
其中最后一节的前缀为
文本。
例如
      • 见下文

        <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
        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)    
        
        将生成以下输出,该输出缺少
          中包含的第二部分:

          {'Data1': {'href': '#ref1'},
           'Data2': {'children': {'Data4': {'children': {'Data5': {'href': '#ref5'},
                                                         'Data6': {'href': '#ref6'}}},
                                           'href': '#ref4'},
                     'href': '#ref2'}
          }    
          

          HTML结构不一致,至少缺少一个开头(或结尾过多)。一定要确保它是干净的。在字典中,您确实需要一个用于测试字典值的键。可能是
          'children'
          ?它们是我想在Future中查找的真实值,例如
          表单
          电子邮件
          模型
          。这不是有效的数据结构。在每组大括号中,您的
          href
          dict都有键,但您的孩子dict没有键。这不是口述,也不是一套,只是一个语法错误。也许你想要像
          'href':'ref2',children:{…
          ?@MartijnPieters是的,你对HTML结构的看法是对的。我收到的数据要多得多,我试图将其缩短。我想可能是错误地添加了两个额外的
        • -元素,这在帖子中得到了修复。感谢你一直以来的帮助,不幸的是,它最终添加了子元素艾姆斯
          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)    
          
          {'Data1': {'href': '#ref1'},
           'Data2': {'children': {'Data4': {'children': {'Data5': {'href': '#ref5'},
                                                         'Data6': {'href': '#ref6'}}},
                                           'href': '#ref4'},
                     'href': '#ref2'}
          }