Introduction to 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 a Navigable Tree
BeautifulSoup is a Python library designed to parse real-world HTML. Its central job is to transform raw HTML text into a navigable structure. Once parsing is complete, HTML elements become nodes in a tree, and you can inspect tags by their names, attributes, text, and position among parent, child, and sibling elements.
- 1Provide the raw HTML string and a parser specification to the BeautifulSoup constructor.
- 2Let the parser recognize tag boundaries and extract attributes.
- 3BeautifulSoup builds an internal tree of elements.
- 4Navigate the resulting object to locate tags and inspect their parts.
Why Real HTML Needs Tolerance
HTML resembles XML because both use tags, attributes, and hierarchical nesting. Their parsing expectations are different, however. XML requires a strict, well-formed structure. Real web pages commonly contain missing closing tags, malformed attributes, or invalid nesting. A strict XML parser can reject such a page as malformed and stop.
BeautifulSoup takes a tolerant approach. When it encounters malformed HTML, it makes assumptions about the author's intended structure, such as inferring where a missing closing tag should occur or doing its best to extract a malformed attribute value. It then builds a navigable tree instead of rejecting the entire page.
| Approach | Response to malformed HTML | Result |
|---|---|---|
| Strict XML parsing | Enforces well-formed structure | May reject the page and stop |
| BeautifulSoup tolerant parsing | Makes assumptions about the intended structure | Builds a navigable tree |
The practical difference is how each approach handles imperfect input.
Installing and Starting the Parser
BeautifulSoup is available on the Python Package Index. The source material identifies pip as the easiest way to install it. After installation, import BeautifulSoup into a Python script and provide HTML text together with a parser specification.
In this sequence, html is the raw input, html.parser is the parser specification, and soup is the resulting BeautifulSoup object. The object represents the parsed HTML as a structure that can be queried and traversed.
Inside a Tag Object
A parsed HTML element becomes a BeautifulSoup Tag object. A Tag is not merely a string representation of the original element. It is a structured object with separately inspectable parts: its name, its attributes, its inner text and child nodes, and its position in the document tree.
| Part | Access pattern | What it provides |
|---|---|---|
| Attribute value | tag.get('href', None) | A requested attribute value, or a default when it is missing |
| All attributes | tag.attrs | The tag's attributes |
| Contents | tag.contents | A list containing text nodes and child Tag objects |
| Clean text | tag.get_text().strip() | Text content with surrounding whitespace removed |
Common inspection targets for a BeautifulSoup Tag.
Finding and Inspecting Links
Anchor elements are represented by the a tag. After parsing, retrieve the anchor tags from the document, then inspect each Tag separately. The URL is stored in the href attribute, the user-visible text can be obtained with get_text(), and the complete attribute collection is available through attrs.
https://example.com
Example
{'href': 'https://example.com'}
/about
About
{'href': '/about', 'class': ['navigation']}
#contact
Contact
{'href': '#contact'}The loop treats every result as a structured Tag. It does not need to parse the original HTML text manually. Instead, it asks the Tag for the specific pieces needed: href for the destination, get_text().strip() for readable text, and attrs for the full attribute collection.
Contents, Text, and Children
A Tag's contents are the items between its opening and closing tags. The contents property returns a list. Each item may be a NavigableString representing text or another Tag representing a nested element. Because the original HTML may contain formatting whitespace, contents can include newline characters as well as child tags.
[\n , <span>Profile</span>, \n]
ProfileThe contents result preserves the structure and whitespace found between the anchor's tags. get_text() is the more convenient choice when the goal is readable text rather than the individual child nodes. Applying strip() removes surrounding whitespace from that extracted text.
Understanding Link Destinations
Real web pages contain more than one kind of href value. An absolute URL includes its complete destination, such as https://example.com/page. A relative path points to a location related to the current page, such as /about. An in-page reference begins with a number sign, such as #contact, and refers to a location within the current page. BeautifulSoup exposes these values, but it does not make them equivalent destinations.
Always filter and validate href values before using them. A scraper should distinguish complete URLs, relative paths, and in-page references instead of treating every href string as a ready-to-request web address.
Common mistake Treating every href as an absolute URL
What it looks likeSending /about to a process that expects a complete web address
Why it failsRelative paths are related to the current page, while absolute URLs include a complete destination.
Do this insteadClassify and validate the href before using it.
Common mistake Assuming every anchor contains an href attribute
What it looks likeReading link['href'] without checking whether href exists
Why it failsDirect attribute access can raise a
KeyErrorwhen the attribute is missing.Do this insteadUse link.get('href', None) when the attribute may be absent.
Common mistake Using contents as if it were clean text
What it looks likePrinting contents and expecting only the visible words
Why it failscontents can include whitespace and nested Tag objects.
Do this insteadUse get_text().strip() for clean text extraction.
A Complete Inspection Routine
Inspect each anchor safely
Given a parsed HTML document, retrieve every anchor tag and report its href value, clean text, and attributes.
- 1Parse
Pass the HTML string and html.parser to BeautifulSoup so the raw input becomes a navigable object.
- 2Retrieve
Search the parsed object for all a tags and receive a collection of Tag objects.
- 3Inspect
For each Tag, use
get('href', None),get_text().strip(), and attrs to access the requested components. - 4Validate
Classify the href as an absolute URL, relative path, or in-page reference before using it.
The routine separates parsing, tag retrieval, component extraction, and URL handling into clear steps.
Write a short routine that parses an HTML string containing three anchor tags: one with an absolute URL, one with a relative path, and one with an in-page reference. For each anchor, print its href value, cleaned text, and complete attributes.
Hints
- Create the BeautifulSoup object with the HTML string and html.parser.
- Retrieve all anchor tags with
find_all('a'). - Use
get('href', None),get_text().strip(), and attrs. - Compare the beginning and shape of each href before treating it as a destination.
What do you think happens?
If an anchor contains a nested span and newline characters, what should you expect from contents compared with get_text().strip()?
Reveal answer
contents preserves text and child nodes, while get_text().strip() returns cleaned text
contents represents the items inside the tag, including whitespace and nested Tag objects. get_text() extracts the rendered text, and strip() removes surrounding whitespace.
Essential Takeaways
- 1BeautifulSoup transforms raw HTML into a navigable tree of Tag objects.
- 2A Tag exposes its name, attributes, contents, text, and relationships in the document tree.
- 3Use
get('href', None)for safe attribute access, attrs for the complete attribute collection, andget_text().strip() for clean text. - 4contents preserves a tag's child structure and may include whitespace or nested tags.
- 5BeautifulSoup's tolerant parsing approach is practical for malformed real-world HTML, but extracted URLs should still be classified and validated.
At a glance
- BeautifulSoup parses raw HTML into a navigable tree rather than leaving it as unstructured text.
- Anchor tags can be retrieved as Tag objects and inspected through href,
get_text(), attrs, and contents. - Use safe attribute access and clean text extraction to handle missing attributes and source whitespace.
- Absolute URLs, relative paths, and in-page references require different handling.
- BeautifulSoup tolerates malformed HTML that strict XML parsers may reject.