Важно отметить, что .next_sibling
также может работать, вам придется использовать некоторую логику, чтобы знать, сколько раз вызывать его, поскольку вам может понадобиться собрать несколько текстовых узлов. В этом примере мне легче ориентироваться в потомках, отмечая важные характеристики, которые сигнализируют мне сделать что-то другое.
Вы просто должны разбить характеристики того, что вы чистите. В этом простом случае мы знаем:
- Когда мы видим элемент
strong
, мы хотим захватить «заголовок».
- Когда мы видим первый элемент
br
, мы хотим начать захват «контента».
- Когда мы видим второй элемент
br
, мы хотим начать захват «дополнительного содержимого».
Мы можем:
- Выберите класс
plans
, чтобы получить все планы.
- Затем мы можем перебрать все узлы-потомки
plans
.
- Если мы увидим тег, посмотрите, соответствует ли он одному из указанных выше условий, и подготовьтесь к захвату текстовых узлов в правильном контейнере.
- Если мы видим текстовый узел и у нас есть готовый контейнер, сохраните текст.
- Удалите ненужные начальные и конечные пробелы и сохраните данные для плана.
from bs4 import BeautifulSoup as bs
from bs4 import Tag, NavigableString
html = """
<p class="plans">
<strong>
TITLE CONTENT #1
</strong>
<br/>
BODY CONTENT #1
<br/>
EXTRA CONTENT #1
</p>
<p class="plans">
<strong>
TITLE CONTENT #2
<br/>
</strong>
BODY CONTENT #2
<br/>
EXTRA CONTENT #2
</p>
<p class="plans">
<strong>
TITLE CONTENT #3
</strong>
<br/>
BODY CONTENT #3
<br/>
EXTRA CONTENT #3
</p>
"""
soup = bs(html, 'html.parser')
content = []
# Iterate through all the plans
for plans in soup.select('.plans'):
# Lists that will hold the text nodes of interest
title = []
body = []
extra = []
current = None # Reference to one of the above lists to store data
br = 0 # Count number of br tags
# Iterate through all the descendant nodes of a plan
for node in plans.descendants:
# See if the node is a Tag/Element
if isinstance(node, Tag):
if node.name == 'strong':
# Strong tags/elements contain our title
# So set the current container for text to the the title list
current = title
elif node.name == 'br':
# We've found a br Tag/Element
br += 1
if br == 1:
# If this is the first, we need to set the current
# container for text to the body list
current = body
elif br == 2:
# If this is the second, we need to set the current
# container for text to the extra list
current = extra
elif isinstance(node, NavigableString) and current is not None:
# We've found a navigable string (not a tag/element), so let's
# store the text node in the current list container.
# NOTE: You may have to filter out things like HTML comments in a real world example.
current.append(node)
# Store the captured title, body, and extra text for the current plan.
# For each list, join the text into one string and strip leading and trailing whitespace
# from each entry in the row.
content.append([''.join(entry).strip() for entry in (title, body, extra)])
print(content)
Затем вы можете распечатать данные так, как хотите, но вы должны получить их в хорошем логическом порядке, как показано ниже:
[['TITLE CONTENT #1', 'BODY CONTENT #1', 'EXTRA CONTENT #1'], ['TITLE CONTENT #2', 'BODY CONTENT #2', 'EXTRA CONTENT #2'], ['TITLE CONTENT #3', 'BODY CONTENT #3', 'EXTRA CONTENT #3']]
Есть несколько способов сделать это, это только один.