Использование BeautifulSoup для нахождения всех элементов "ul" и "li" - PullRequest
0 голосов
/ 14 мая 2018

В настоящее время я работаю над сценарием сканирования в 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>

Поскольку это внешний сайт, я не могу контролироватьИдентификатор или класс элементов в списке.

Кажется, я не могу разобраться с этим, есть ли простой способ упорядочить данные в список или dict?:

dict = {'Data1': {'href': 'ref1'}, 
        'Data2': {'href': 'ref2', {
                  'Data3': {'href': 'ref3'}, 
                  'Data4': {'href': 'ref4', {
                            'Data5': {'href': 'ref5'},
                            'Data6': {'href': 'ref6'},
                                    .
                                    .
                                    .                }
                                    }
                       }
               }
       }

Я чувствую, что это громоздкий процесс, однако я не вижу другого способа сделать это.

Любая помощь, чтобы заставить меня двигаться в правильном направлении, очень ценится!

Ура!

Ответы [ 2 ]

0 голосов
/ 14 мая 2018

Нет тривиального способа сделать это, но это не так уж и громоздко.

Например, вы можете сделать это рекурсивно, что-то вроде этого:

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

(Я должен был дать каждому из этих children диктов ключ, потому что структура, которую вы на самом деле хотели, не является действительным диктом.)

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

0 голосов
/ 14 мая 2018

Просто выделите элемент ul, вытянув текст всех элементов li, имеющих текст, повторяясь глубже, если вместо него есть элемент <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

Это займет всепрямые li элементы;если есть элемент <a>, то текст этого элемента привязки превращается в ключ, и мы сохраняем копию атрибутов тега в качестве значения (игнорируя любые атрибуты class).Если рядом с тегом a есть также a <ul> элемент, он рекурсивно анализируется и добавляется как ключ children в словарь атрибутов для тега <a>.

Для вашего образца ввода это производит:

>>> 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'}}
...