Как определить, существует ли дочерний тег ‘span’ в теге ‘p’, возвращаемом beautifulsoup?

#html #python-3.x #if-statement #beautifulsoup

#HTML #python-3.x #if-оператор #beautifulsoup

Вопрос:

Я создаю веб-скребок, который удаляет онлайн-роман с веб-страницы индекса, и код создает файл epub для каждой книги романа. Переводчик романа настроил веб-страницы для романа в двух разных форматах.

Первый формат — это p тег с span тегом внутри. span тег содержит множество css для каждого раздела абзацев, в зависимости от того, является ли он обычным текстом или инициализируется.

Другой формат — это текст в p теге без span тега и без css-кода. Я смог использовать Beautifulsoup для получения части кода, содержащей только роман с веб-страницы. Я застрял, пытаясь создать if утверждение, в котором говорится, что если span существует внутри содержимого главы, запустите один код, иначе запустите какой-нибудь другой код.

Я пытался использовать if chapter.find('span') != []: и if chapter.find_all('span') != []: из beautifulsoup, но эти коды beautifulsoup возвращают фактические значения, а не логические значения. Я протестировал это, напечатав ‘yes’ или ‘no’, если у главы был тег, но вывод был бы либо только ‘yes’, либо только ‘no’, когда я проверил 2 разные главы на соответствие тому, что у них нет разных форматов.

Код, который я использую:

     #get link for chapter 1 from index
    r = requests.get(data[1]['link'])
    soup = BeautifulSoup(r.content, 'html.parser')

    # if webpage announcement change 0 to 1
    chapter = soup.find_all('div', {"class" : "fr-view"})[0].find_all('p')
  

В зависимости от главы, вывод либо:

     #chapter equals this
    [<p dir="ltr"><span style="color: rgb(0, 0, 0); background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap">Chapter 1 - title</span></p>,
    <p dir="ltr"><span style="color: rgb(0, 0, 0); background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap">stuff</span></p>,
    <p dir="ltr"><span style="color: rgb(0, 0, 0); background-color: transparent; font-weight: 400; font-style: italic; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap">italizes</span><span style="color: rgb(0, 0, 0); background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap"> stuff</span></p>]

  

или:

     #chapter equals this
    [<p>Chapter 6 - title</p>,
    <p>stuff</p>]
  

Я пытаюсь создать оператор and if , который может прочитать главу и сообщить мне, завершается ли span тег, чтобы я мог выполнить правильный код.

Ответ №1:

В Beautiful Soup 4.7 Beautiful Soup использует новую библиотеку селекторов CSS под названием Soup Sieve. Использование find_all и find — прекрасный способ условной фильтрации ваших тегов, но мне нравится демонстрировать альтернативный способ, который вы можете использовать для выполнения сложной фильтрации с помощью CSS-селекторов. Soup Sieve предоставляет ряд полезных функций, и поскольку Beautiful Soup полагается на него, он уже должен быть установлен, если вы используете Beautiful Soup 4.7 .

В этом случае мы просто ищем p теги, а затем напрямую используем API Soup Sieve для создания фильтра для сравнения возвращенных тегов. Просто еще один способ сделать что-то.

 from bs4 import BeautifulSoup
import soupsieve as sv

html = """

<p dir="ltr"><span style="color: rgb(0, 0, 0); background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap">Chapter 1 - title</span></p>,
<p dir="ltr"><span style="color: rgb(0, 0, 0); background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap">stuff</span></p>,
<p dir="ltr"><span style="color: rgb(0, 0, 0); background-color: transparent; font-weight: 400; font-style: italic; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap">italizes</span><span style="color: rgb(0, 0, 0); background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap"> stuff</span></p>

<p>Chapter 6 - title</p>,
<p>stuff</p>
"""

soup = BeautifulSoup(html, "html.parser")
css_match = sv.compile(':has(span)')
for i in soup.select('p'):
    if css_match.match(i):
        print('found span')
    else:
        print('no span')
  

Вывод

 found span
found span
found span
no span
no span
  

Ответ №2:

Используя ваши фрагменты кода:

 html = """

<p dir="ltr"><span style="color: rgb(0, 0, 0); background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap">Chapter 1 - title</span></p>,
<p dir="ltr"><span style="color: rgb(0, 0, 0); background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap">stuff</span></p>,
<p dir="ltr"><span style="color: rgb(0, 0, 0); background-color: transparent; font-weight: 400; font-style: italic; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap">italizes</span><span style="color: rgb(0, 0, 0); background-color: transparent; font-weight: 400; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap"> stuff</span></p>

<p>Chapter 6 - title</p>,
<p>stuff</p>
"""
  

Вы можете попробовать это:

 soup = BeautifulSoup(html, "lxml")
my_p = soup.find_all('p')
for i in my_p:
    if i.find('span'):
        print('found span')
    else:
        print('no span')
  

Вывод:

 found span`
found span
found span
no span
no span`
  

Я думаю, это то, что вы ищете.

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

1. Я пробовал этот метод, но он выводил только ‘found spam’ в обоих форматах. Я думаю, это потому, что i.find('spam') это не логическая функция, а функция, которая возвращает строковое значение span тега