</>
Тема 4 из 5

Навигация по дереву и таблицы

HTML-документ — дерево: у каждого тега есть родитель (тег, внутри которого он лежит), могут быть дети (теги и текст внутри) и соседи (другие дети того же родителя). По этому дереву можно ходить во все стороны.

<div class="card">
  <h3>Чайник</h3>
  <p>Цена: <span>1500</span></p>
</div>

Здесь у <span> родитель — <p>, у <p> родитель — <div>. <h3> и <p> — соседи.

Вверх: родители

span = soup.find("span")
print(span.parent.name)                    # p
print([t.name for t in span.parents])      # ['p', 'div', '[document]']
  • .name — имя тега;
  • .parent — непосредственный родитель;
  • .parents — все предки снизу вверх. Последний из них — сам объект BeautifulSoup с именем [document].

Подниматься вверх удобно, когда нашли что-то внутри и нужен весь блок: нашли цену — поднялись до карточки товара и взяли из неё название. Для этого есть и find_parent("div") — ближайший предок нужного вида.

Вниз: дети

div = soup.find("div")
for child in div.children:      # непосредственные дети — теги и текст
    print(repr(child.name))     # None для текста, 'h3', 'p'
print(div.find_all(recursive=False))   # только теги-дети, без текста

.children перебирает непосредственных детей, включая текстовые узлы — переводы строк и пробелы между тегами. Если нужны только теги, удобнее find_all(recursive=False). Всех потомков на любой глубине даёт .descendants или обычный find_all().

Вбок: соседи

h3 = soup.find("h3")
print(h3.find_next_sibling())            # <p>Цена: ...</p> — следующий сосед-тег
print(h3.find_next_sibling("p"))         # ближайший следующий <p>
print(h3.find_next_siblings())           # все следующие соседи
print(h3.find_previous_sibling())        # предыдущий (здесь None)

Есть и свойства .next_sibling / .previous_sibling, но они возвращают и текстовые узлы, поэтому часто дают перевод строки вместо тега. Методы find_next_sibling(...) пропускают текст.

Соседи помогают, когда в разметке нет вложенности, а смысл задаётся порядком: заголовок, а за ним абзацы до следующего заголовка; термин <dt>, а за ним его определения <dd>.

Таблицы

Таблица в HTML — <table>, строки — <tr>, ячейки — <th> (заголовочная) и <td> (обычная). Строки часто лежат в <thead> и <tbody>, но find_all("tr") находит их на любой глубине:

table = soup.find("table")
rows = []
for tr in table.find_all("tr"):
    cells = [cell.get_text().strip() for cell in tr.find_all(["th", "td"])]
    rows.append(cells)
header, data = rows[0], rows[1:]

find_all(["th", "td"]) возвращает ячейки обоих видов в порядке документа. Готовые таблицы так легко перенести в pandas: pd.DataFrame(data, columns=header).

Итог

  • .parent, .parents, find_parent(...) — вверх;
  • .children, find_all(recursive=False), .descendants — вниз;
  • find_next_sibling(...), find_next_siblings(...), find_previous_sibling(...) — вбок;
  • таблица: tr → find_all(["th", "td"]) → get_text().strip().