Python/BeautifulSoup – Как извлечь текст между тегами ‹li› и ‹dl›

У меня есть следующий HTML-код

<ol>
<li>If someone is <b>able</b> to do something, they <a href="/wiki/can" title="can">can</a> do it.
<dl>
<dd><i>I'm busy today, so I won't be <b>able</b> to see you.</i></dd>
</dl>
</li>
</ol>

Как я могу извлечь текст между тегами <li> и <dl>.

Я пробовал это:

from bs4 import BeautifulSoup

s = """<ol>
    <li>If someone is <b>able</b> to do something, they <a href="/wiki/can" title="can">can</a> do it.
    <dl>
    <dd><i>I'm busy today, so I won't be <b>able</b> to see you.</i></dd>
    </dl>
    </li>
    </ol>
"""

soup = BeautifulSoup(s)

for line in soup.find_all('ol'):
    print line.li.get_text()

Это напечатает

If someone is able to do something, they can do it.

I'm busy today, so I won't be able to see you.

Мне нужна только первая строка.

If someone is able to do something, they can do it.

person Vinod    schedule 09.09.2013    source источник


Ответы (1)


Перебрать потомков объекта line.li, собрать все NavigableString текстовых объектов , и когда вы встретите тег <dl>, остановитесь:

from bs4 import NavigableString

for line in soup.find_all('ol'):
    result = []
    for descendant in line.li.descendants:
        if isinstance(descendant, NavigableString):
            result.append(unicode(descendant).strip())
        elif descendant.name == 'dl':
            break

    print u' '.join(result)

Демо:

>>> for line in soup.find_all('ol'):
...     result = []
...     for descendant in line.li.descendants:
...         if isinstance(descendant, NavigableString):
...             result.append(unicode(descendant).strip())
...         elif descendant.name == 'dl':
...             break
...     print u' '.join(result)
... 
If someone is able to do something, they can do it.

Если вы хотите сделать это для всех тегов <li> (а не только для первого), вам нужно перебрать теги <li>, найденные с помощью .find_all():

for line in soup.find_all('ol'):
    for item in line.find_all('li'):
        result = []
        for descendant in item.descendants:
            if isinstance(descendant, NavigableString):
                result.append(unicode(descendant).strip())
            elif descendant.name == 'dl':
                break

        print u' '.join(result)
person Martijn Pieters    schedule 09.09.2013
comment
Куда делись слова могу и могу? Должно быть - если кто-то "может" что-то сделать, он "может" это сделать. - person Vinod; 09.09.2013
comment
@Vinod: хм, мы где-то потеряли важный атрибут .descendants. :-) - person Martijn Pieters; 09.09.2013
comment
@Vinod: исправлено и протестировано повторно; мои извинения, не знаю, как я пропустил это. - person Martijn Pieters; 09.09.2013
comment
Если тег ‹ol› содержит несколько ‹li› с ‹dl›, он будет работать. Я тестировал с <ol> <li>Around, near. <dl> <dd><i>He walked <b>about</b> the place, looking everywhere.</i></dd> </dl> </li> <li>(<i>numbers</i>) Close to; approximately; near; like. <dl> <dd><i>He was <b>about</b> thirteen years old.</i></dd> </dl> </li> <li>Having to do with, concerning, regarding. <dl> <dd><i>I will talk <b>about</b> dogs.</i></dd> </dl> </li> </ol> Наша программа печатает Вокруг, рядом. Только. Остальные два ‹li› пропали без вести. - person Vinod; 09.09.2013
comment
.descendants перечислены только дочерние узлы в порядке документа, а код останавливается на первом дочернем теге <dl>. Строка line.li из вашего исходного примера обрабатывает только первый тег <li> в теге <ol>, но мой код будет нормально работать и в цикле по line.find_all('li'). - person Martijn Pieters; 09.09.2013