Получить весь текст внутри одного тега с помощью BeautifulSoup?

#python-3.x #web-scraping #beautifulsoup

#python-3.x #очистка веб-страниц #beautifulsoup

Вопрос:

Каков способ получить текст для d2 и d3 в следующем случае:

 import bs4

htmldoc = '''<html>
<div class="a">
    <div class="b">abc def1</div>
    <div class="c">123</div>
</div>
<div class="a">
    <div class="b">abc def<sup>2</sup></div>
    <div class="c">456</div>
</div>
<div class="a">
    <div class="b">abc <!-- -->def3</div>
    <div class="c">789</div>
</div>
</html>'''

soup = bs4.BeautifulSoup(htmldoc, 'html.parser')
d1 = soup.find('div', class_ = 'b', text = 'abc def1').findNext('div').text
print(d1)
d2 = soup.find('div', class_ = 'b', text = 'abc def2').findNext('div').text
print(d2)
d3 = soup.find('div', class_ = 'b', text = 'abc def3').findNext('div').text
print(d3)
  

Это работает только для d1, но для d2 и d3 возникает ошибка…

Ответ №1:

Это потому, что ваше правило сопоставления с текстом не работает с текстом

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

Прежде чем удалять неиспользуемые теги и комментарии, используйте методы unwrap и extract

 import bs4

htmldoc = '''<html>
<div class="a">
    <div class="b">abc def1</div>
    <div class="c">123</div>
</div>
<div class="a">
    <div class="b">abc def<sup>2</sup></div>
    <div class="c">456</div>
</div>
<div class="a">
    <div class="b">abc <!-- -->def3</div>
    <div class="c">789</div>
</div>
</html>'''


def get_new_soup():
    soup = bs4.BeautifulSoup(htmldoc, 'html.parser')
    divs_b = soup.find_all('div',{'class','b'})
    for div in divs_b:
        # Remove unwanted tag (like <sup> here)
        if div.sup:
            div.sup.unwrap()
        # Remove comments
        for element in div(text=lambda text: isinstance(text, bs4.Comment)):
            element.extract()

    soup_str = str(soup)
    return(bs4.BeautifulSoup(soup_str,'html.parser'))

soup = get_new_soup()

d1 = soup.find('div', class_ = 'b', text = 'abc def1').findNext('div').text
print(d1)
d2 = soup.find('div', class_ = 'b', text = 'abc def2').findNext('div').text
print(d2)
d3 = soup.find('div', class_ = 'b', text = 'abc def3').findNext('div').text
print(d3)
  

ВЫВОД:

 123
456
789
  

Редактировать:

Как и было запрошено в комментарии, я вижу единственный способ получить ваши данные следующим образом:

 import bs4

htmldoc = '''<html>
<div class="a">
    <div class="b">abc def1</div>
    <div class="c">123</div>
</div>
<div class="a">
    <div class="b">abc def<sup>2</sup></div>
    <div class="c">456</div>
</div>
<div class="a">
    <div class="b">abc <!-- -->def3</div>
    <div class="c">789</div>
</div>
</html>'''


def get_new_soup():
    soup = bs4.BeautifulSoup(htmldoc, 'html.parser')
    divs_b = soup.find_all('div',{'class','b'})
    for div in divs_b:
        # Remove comments
        for element in div(text=lambda text: isinstance(text, bs4.Comment)):
            element.extract()

    soup_str = str(soup)
    return(bs4.BeautifulSoup(soup_str,'html.parser'))

soup = get_new_soup()

search_text = ['abc def1', 'abc def<sup>2</sup>', 'abc def3']

divs = soup.find_all('div', class_ = 'b')
for div in divs:
    content = ''.join(str(c) for c in div.contents)
    if content in search_text:
        print(div.findNext('div').text)
  

ВЫВОД:

 123
456
789
  

Комментарии:

1. Есть ли способ сделать что-то подобное: d2 = soup.find('div', class_ = 'b', text = 'abc def<sup>2</sup>').findNext('div').text

2. Вы можете проверить мою правку. Вы можете объединить первый цикл внутри get_new_soup , а второй мне нравится

Ответ №2:

Возможно, я что-то упускаю, но делаю это:

 d1 = soup.find_all('div', class_ = 'c')
for i in (d1):
   print(i.text)
  

Выдает такой результат:

 123
456
789
  

Это то, что вы ищете?

Комментарии:

1. Мне нужны значения для d1, d2 и d3, но порядок не является постоянным. Итак, мне нужно заглянуть внутрь текста предыдущего тега.