HTML
web development
programming
list tags
parsing

Parse 'ul' and 'ol' tags

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Parsing ul and ol tags usually means extracting structured list data from HTML without losing the relationship between list items and nested lists. The main challenge is that HTML lists can be deeply nested, mixed with inline tags, and sometimes malformed.

The safest approach is to use an HTML parser rather than regular expressions. Once the document is parsed into a tree, you can walk ul, ol, and li nodes predictably.

That tree-based approach is what makes ordered lists, unordered lists, and nested combinations manageable without brittle text slicing.

Parse Lists with BeautifulSoup

In Python, BeautifulSoup is a practical choice for HTML list parsing:

python
1from bs4 import BeautifulSoup
2
3html = """
4<div>
5  <ul>
6    <li>Apples</li>
7    <li>Bananas</li>
8    <li>Citrus
9      <ol>
10        <li>Orange</li>
11        <li>Lemon</li>
12      </ol>
13    </li>
14  </ul>
15</div>
16"""
17
18soup = BeautifulSoup(html, "html.parser")
19
20for list_tag in soup.find_all(["ul", "ol"]):
21    print(list_tag.name)

This gives you actual element nodes instead of raw text fragments, which is what makes nested-list parsing manageable.

Extract Only Direct List Items

One common mistake is calling find_all("li") on a list and accidentally collecting items from nested lists too. Use recursive=False when you only want direct children:

python
1from bs4 import BeautifulSoup
2
3def extract_direct_items(list_tag):
4    items = []
5    for li in list_tag.find_all("li", recursive=False):
6        text = li.find(string=True, recursive=False)
7        items.append(text.strip() if text else "")
8    return items
9
10soup = BeautifulSoup(html, "html.parser")
11root_list = soup.find("ul")
12print(extract_direct_items(root_list))

That keeps the top-level list separate from any nested ul or ol inside an item.

Preserve Nested Structure

If the goal is to build a structured representation, recursion is the cleanest solution:

python
1from bs4 import BeautifulSoup
2
3def parse_list(list_tag):
4    result = {
5        "type": list_tag.name,
6        "items": []
7    }
8
9    for li in list_tag.find_all("li", recursive=False):
10        item = {"text": "", "children": []}
11
12        direct_text_parts = []
13        for child in li.contents:
14            if getattr(child, "name", None) in ("ul", "ol"):
15                item["children"].append(parse_list(child))
16            elif isinstance(child, str):
17                direct_text_parts.append(child.strip())
18
19        item["text"] = " ".join(part for part in direct_text_parts if part)
20        result["items"].append(item)
21
22    return result
23
24soup = BeautifulSoup(html, "html.parser")
25data = parse_list(soup.find("ul"))
26print(data)

This kind of representation is useful when you want to render the list in another format such as JSON, Markdown, or a custom UI tree.

Handle Inline Markup Inside li

Real list items often contain links, emphasis, or spans:

html
<li><strong>Important:</strong> Read the docs</li>

If you want the visible text, use get_text:

python
item_text = li.get_text(" ", strip=True)

That joins inline fragments with spaces and strips surrounding whitespace. It is usually better than manually reading only the first text node unless you specifically need to separate inline content from nested lists.

Choose Output Shape Before Coding

There are several valid answers to "parse a list":

  • extract plain text only
  • preserve whether the list was ordered or unordered
  • keep nested structure
  • flatten everything into one sequence

The parser is easier to write once the output contract is clear. Many bugs come from not deciding that up front.

Common Pitfalls

  • Using regular expressions for nested HTML list parsing.
  • Collecting all descendant li elements when only direct children were intended.
  • Losing whether a list was ul or ol.
  • Dropping nested list structure when the output format actually needs it.
  • Ignoring inline markup and ending up with partial or fragmented text.

Summary

  • Use an HTML parser, not regex, to parse ul and ol tags reliably.
  • Use recursive=False when you want only direct list items.
  • Use recursion to preserve nested list structure.
  • Decide whether you need plain text, list type, nesting, or all three.
  • 'get_text is useful when list items contain inline HTML markup.'

Course illustration
Course illustration

All Rights Reserved.