Tags, attributes and text

Beautiful Soup · Essentials · 10 min

Extracting data means reading attributes like href and the text inside tags correctly.

Each element in the tree is a Tag. Accessing soup.a returns the first <a> anywhere below; chaining like soup.body.p goes one level of "first match" at a time.

Attributes work like a dict:

  • tag["href"] returns the value, raising KeyError if absent.
  • tag.get("href") returns None (or a default) instead: use it for optional attributes.
  • tag.attrs is the full dict; tag.has_attr("data-id") tests presence.
  • Multi-valued attributes, most importantly class, come back as a list: ["btn", "primary"].

Text has several accessors:

  • tag.string: the single string child, or None when the tag contains more than one child (such as text plus a <b>).
  • tag.get_text(): all text inside, concatenated. Use get_text(" ", strip=True) to join pieces with a space and strip whitespace, which is what you usually want.
  • tag.stripped_strings: a generator over non-blank text pieces.

tag.name is the tag name ("a"), and you can rename it by assignment. Entities like &amp; are decoded for you: text is plain Unicode.

Most bugs at this stage are about None: a missing tag (soup.find(...) returning None) or .string being None. Check before chaining.

Example

from bs4 import BeautifulSoup

html = """<div class="card featured" data-id="42">
  <a href="/item/42" title="Lamp">Desk <b>lamp</b> &amp; bulb</a>
  <span class="price"> 19.99 </span>
</div>"""
soup = BeautifulSoup(html, "html.parser")
card, link = soup.div, soup.a

print(card["class"])               # ['card', 'featured']
print(card["data-id"], card.get("missing"))   # 42 None
print(link.attrs)                  # {'href': '/item/42', 'title': 'Lamp'}
print(link.string)                 # None: several children
print(link.get_text())             # Desk lamp & bulb
print(soup.find("span").get_text(strip=True))   # 19.99
print(list(card.stripped_strings)) # ['Desk', 'lamp', '& bulb', '19.99']

Remember

  • tag['x'] raises on missing attributes; tag.get('x') returns None
  • class is a list of classes
  • .string is None when a tag has several children; use get_text()
  • get_text(' ', strip=True) for clean text

Watch out for

  • Calling .string on a tag with nested markup and getting None.

Check yourself

  1. For <p>Hello <b>world</b></p>, what is p.string?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.crummy.com/software/BeautifulSoup/bs4/doc/#kinds-of-objects