Атрибуты и ссылки
У тегов бывают атрибуты — дополнительные сведения внутри открывающего тега: адрес ссылки, путь к картинке, класс оформления.
<a href="/catalog" class="menu-link" target="_blank">Каталог</a>
<img src="cat.jpg" alt="Рыжий кот" width="300">
Здесь у ссылки атрибуты href, class и target, у картинки — src, alt и width.
Чтение атрибутов
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, "html.parser")
a = soup.find("a")
print(a["href"]) # /catalog
print(a.get("target")) # _blank
print(a.get("rel")) # None — такого атрибута нет
print(a.get("rel", "нет")) # нет — значение по умолчанию
print(a.attrs) # словарь всех атрибутов
print(a.has_attr("href")) # True
tag["имя"]— как у словаря; если атрибута нет — ошибкаKeyError;tag.get("имя")— безопасный вариант: вернётNoneили указанное значение по умолчанию;- значения атрибутов — строки (числа нужно переводить самим:
int(img["width"])); - атрибут
class(и некоторые другие, где может быть несколько значений через пробел) возвращается списком:['menu-link'].
Атрибут без значения (<input disabled>) или с пустым (alt="") возвращается пустой строкой. Пустая строка в условии if считается ложью — это удобно для проверок.
Поиск по атрибутам
В find и find_all можно указать нужные атрибуты:
soup.find_all("a", href=True) # только ссылки, у которых есть href
soup.find_all("img", alt="") # картинки с пустым alt
soup.find("input", attrs={"name": "q"}) # по произвольному атрибуту
soup.find_all("div", class_="card") # по классу — с подчёркиванием!
soup.find(id="main") # по id, тег любой
class — зарезервированное слово Python, поэтому параметр называется class_. Поиск по классу находит теги, у которых среди классов есть указанный: <div class="card big"> тоже подходит.
Полные адреса: urljoin
В href часто записан относительный адрес — относительно текущей страницы. Чтобы получить полный, используют urljoin из стандартной библиотеки:
from urllib.parse import urljoin
page = "https://shop.ru/catalog/tea.html"
print(urljoin(page, "green.html")) # https://shop.ru/catalog/green.html
print(urljoin(page, "../about")) # https://shop.ru/about
print(urljoin(page, "/cart")) # https://shop.ru/cart
print(urljoin(page, "https://x.ru")) # https://x.ru — уже полный
green.html— в той же папке, что и текущая страница;../— на папку выше;/cart— от корня сайта;- полный адрес остаётся как есть.
Склеивать адреса вручную через + — частая ошибка: правил много, и urljoin знает их все.
Итог
tag["имя"]иtag.get("имя", по_умолчанию)— значение атрибута;class— список классов;find_all("a", href=True),class_="...",id="...",attrs={...}— поиск по атрибутам;urljoin(страница, href)— полный адрес ссылки.