Finding and Selecting Tags with BeautifulSoup
Tag inspection in BeautifulSoup means accessing the individual components of a Tag object: its attributes (via get() or attrs), its text content (via contents or get_text()), and its child elements (via contents).
Try it: Parsing HTML with BeautifulSoup
How BeautifulSoup(html, 'html.parser') turns raw HTML text into a tree of Tag objects, tolerating unclosed and mis-nested tags, and how soup('a'), find() and find_all() with class_ / attrs filters pick tags out of it so .get('href', None), tag['href'], .attrs, .contents, .get_text() and .string can extract their data.
How it works
- html.parser reads the text left to right and reports start tags (with their attributes), end tags, text, comments and declarations.
- Each start tag becomes a Tag inside the tag that is currently open; void elements such as <br> and <img> are closed at once; class and rel values are split into lists.
- An end tag closes the most recently opened tag of that name, and every tag still open inside it; an end tag with no matching open tag is ignored. Tags still open at the end are closed then. Whitespace-only text becomes a single '\n' or ' ' string.
- soup('a') is the same call as soup.find_all('a'): it walks every tag in document order and keeps those whose name matches and whose attributes pass the filter (a class_ value matches any one class). find() stops at the first match and returns None when nothing matches.
- Each result is a Tag: .get('href', None) returns the value or None, tag['href'] raises KeyError when it is missing, .attrs is the attribute dict, .contents the list of children, .get_text() all the text inside, .string the single string child (or None).
Default run (69 steps): Raw HTML: 356 characters of plain text. BeautifulSoup(html, 'html.parser') will turn it into a tree. … [tag.get('href', None) for tag in soup('a')] → ['https://www.py4e.com/', '/lessons', '#top', None]
Simplified: A from-scratch port of Python 3.12's html.parser and BeautifulSoup 4.15's html.parser tree builder, checked against the real libraries on 330+ documents and 12,000+ queries. Documents are limited to 1,500 characters; named character references are limited to about 50 common names (others stay literal text); searches take one tag name and at most one attribute filter (no regular expressions, lists, functions, string= or CSS select()). Other parsers (lxml, html5lib) build different trees for broken HTML.
Loading the simulation…
From HTML Text to Inspectable Tags
BeautifulSoup does more than preserve HTML as a string. When raw HTML is parsed, BeautifulSoup builds a tree and returns Tag objects for elements in that tree. An anchor element, written with the a tag, therefore becomes a structured object whose name, attributes, inner content, and position among other elements can be inspected separately.
The central idea is to stop treating an anchor as opaque HTML text. Inspect the Tag object to retrieve the href value, the text a user sees, the complete attribute collection, or nested child elements.
The Parsed Document Tree
Parsing comes first. The html.parser reads the HTML string, recognizes tag boundaries, extracts attributes, and builds a navigable tree. Only after this tree exists can you find individual anchor tags and inspect their parts.
The find_all("a") operation selects every anchor tag in the parsed document. The result is a collection of matching Tag objects, so each item can then be inspected independently.
Inside an Anchor Tag
A Tag maintains several related parts. Its tag name identifies the element, its attributes hold key-value information such as href, class, or id, and its inner content contains text and possibly nested elements. The Tag also participates in the document tree through parent, sibling, and child relationships.
Think of the Tag as a small inspection surface rather than a single value. The same object can answer different questions: What element is this? What attributes does it have? What child nodes are inside it? What text would a user read?
| Question | Tag part or operation | What it provides |
|---|---|---|
| What element is this? | Tag name | The element name, such as a |
| What URL is attached? | get('href', None) | The href value, or None when href is absent |
| What attributes exist? | attrs | The tag's attributes |
| What nodes are inside? | contents | A list containing text and child elements |
| What visible text is present? | get_text().strip() | Text with surrounding whitespace removed |
Common inspection questions and the corresponding BeautifulSoup access pattern
Reading Attributes and Text
Inspecting one anchor
Given an anchor Tag created from HTML, retrieve its href, visible text, and attributes without failing when href is missing.
- 1Retrieve the URL safely
Use
tag.get('href', None). The get method returns the attribute value when href exists and returns the supplied default when it does not. - 2Clean the visible text
Use
tag.get_text().strip(). The strip operation removes surrounding whitespace that may have come from formatting in the original HTML. - 3Inspect all attributes
Use tag.attrs to examine the complete attribute collection. The source material describes this collection as attribute entries represented as tuples inside a list.
- 4Inspect the inner structure
Use tag.contents when you need the individual text and child-element items between the opening and closing anchor tags.
One Tag object can provide a safe URL value, cleaned visible text, all attributes, and the list of inner content items.
url: /guide
text: Read the guide
attributes: href and class entries
contents: whitespace text, followed by Read the guide text, followed by whitespace textThe important distinction is that contents preserves the inner structure. Its list may include NavigableString text nodes, whitespace, and nested Tag objects. get_text() instead gives you the rendered text from the tag's content; applying strip() is useful when the HTML contains indentation or line breaks.
URL Forms in Real Pages
An anchor's href is not always a complete web address. Real pages can contain absolute URLs, relative paths, and in-page references. Inspect the href value before deciding how your program should use it, and filter and validate URLs before using them.
| Href form | Example | Inspection implication |
|---|---|---|
| Absolute URL | https://example.com/page | The href contains a complete web address |
| Relative path | /guide | The href identifies a path relative to the page context |
| In-page reference | #details | The href points to a location within the page |
The three URL forms emphasized in the source material
Do not assume every href is ready to fetch or use as an external address. First retrieve it safely, inspect which form it has, and filter and validate it for the task your program is performing.
Mistakes During Inspection
Common mistake Assuming every anchor has an href attribute.
What it looks likeurl = tag['href']Why it failsDirect dictionary-style access raises a
KeyErrorwhen the attribute is missing.Do this insteadUse tag.get('href', None) when the attribute may not exist.
Common mistake Using contents as if it were already clean visible text.
What it looks liketext = tag.contentsWhy it failscontents is a list and may include whitespace, text nodes, and child tags.
Do this insteadUse tag.get_text().strip() when the goal is clean text extraction.
Common mistake Treating an href as if it always were an absolute URL.
What it looks likeUsing /guide and #details exactly like complete web addresses.
Why it failsReal web pages contain absolute URLs, relative paths, and in-page references.
Do this insteadInspect, filter, and validate the href before using it.
Common mistake Treating a Tag as only its printed HTML.
What it looks likeExamining only the string representation of an anchor.
Why it failsThe Tag separately holds its name, attributes, inner content, and document-tree relationships.
Do this insteadInspect the relevant Tag properties and methods directly.
A Small Inspection Routine
from bs4 import BeautifulSoup
html = """
<a href="https://example.com">External page</a>
<a href="/guide">Guide</a>
<a href="#details">Details</a>
<a>Missing URL</a>
"""
soup = BeautifulSoup(html, "html.parser")
for tag in soup.find_all("a"):
href = tag.get("href", None)
text = tag.get_text().strip()
print(href, text)- 1The parser creates a document tree from the HTML.
- 2The loop visits every anchor Tag returned by
find_all('a'). - 3The href value may be an absolute URL, a relative path, an in-page reference, or None.
- 4The text value is obtained separately from the URL and surrounding whitespace is removed.
- BeautifulSoup parses the HTML string with html.parser.
find_all('a')returns each anchor Tag.- get retrieves href safely, including for the anchor with no href.
get_text().strip() extracts clean visible text.
For each anchor in the routine, classify the href as an absolute URL, relative path, in-page reference, or missing attribute. Then explain why get('href', None) is safer than direct dictionary-style access.
Hints
- Look at the beginning of each href value.
- A missing href produces the default value None.
- Use
get_text().strip() for the visible text.
Inspection Checklist
- 1Parse the HTML with BeautifulSoup and html.parser.
- 2Use
find_all('a')to retrieve all anchor Tag objects. - 3Use
get('href', None)for safe URL retrieval. - 4Use
get_text().strip() for clean visible text. - 5Use contents when you need the individual inner text and child-element items.
- 6Use attrs to inspect the complete attribute collection.
- 7Classify, filter, and validate href values because pages contain multiple URL forms.
Key Takeaways
- BeautifulSoup turns parsed HTML elements into structured Tag objects.
find_all('a')retrieves the anchor tags in a parsed document.- A Tag exposes attributes, inner content, child elements, and clean text through separate access patterns.
- Use
get('href', None)for safe attribute access andget_text().strip() for clean text. - Inspect and validate href values because anchors may contain absolute URLs, relative paths, or in-page references.