[Python] Generating Recursive Data Structure From Table

Discussão em 'Python' iniciado por Stack, Junho 20, 2025.

  1. Stack

    Stack Membro Participativo

    I'm trying to convert this Wikipedia table into a nested JSON object.

    I want to group each entry into its parent category. e.g.,

    {
    "name": "Mining, quarrying, and oil and gas extraction",
    "value": 593300,
    "children": [
    {
    "name": "Oil and gas extraction",
    "value": 118500,
    "children": []
    },
    {
    "name": "Mining, except oil and gas",
    "value": 189700,
    "children": [
    {
    "name": "Coal mining",
    "value": 44100,
    "children": []
    },
    {
    "name": "Metal ore mining",
    "value": 44100,
    "children": []
    },
    {
    "name": "Nonmetallic mineral mining and quarrying",
    "value": 102700,
    "children": []
    },
    ]
    },
    {
    "name": "Support activities for mining",
    "value": 290100,
    "children": []
    }
    ]
    }



    Using bs4 to parse the page, I can iterate through the table rows as a list and use the "style: padding" attribute to determine the indentation level*, but I'm unable to convert the indentation into a meaningful parent/child representation.

    I've tried iterating through the list with a recursive approach:

    import bs4

    f = open("./webpage.html", "r", encoding="utf8")
    soup = bs4.BeautifulSoup(f, "html.parser")

    table = soup.find(
    "table", attrs={"class": "collapsible wikitable mw-collapsible mw-made-collapsible"}
    )
    rows = table.find_all("tr")

    stash = []
    def recurse(start_idx):
    for idx, row in enumerate(rows[start_idx:]):
    cols = row.find_all("td")

    # skip headers
    if len(cols) == 0:
    continue
    if not cols[0].attrs['style']:
    continue

    indent = int(cols[0].attrs["style"].strip(';')[-3])
    entry = {
    "name": cols[0].text,
    "value": cols[1].text,
    "indent": indent,
    "children": [],
    }
    while stash:
    parent = stash[-1]
    for inner_idx, inner_row in enumerate(rows[start_idx + idx + 1:]):
    inner_cols = inner_row.find_all("td")
    inner_indent = int(inner_cols[0].attrs["style"].strip(';')[-3])
    inner_entry = {
    "name": inner_cols[0].text,
    "value": inner_cols[1].text,
    "indent": inner_indent,
    "children": [],
    }

    if indent == parent["indent"]:
    child = stash.pop()
    stash[-1]["children"].append(child)
    stash.append(entry)

    if parent["indent"] - indent == -1:
    stash.append(entry) # add row to stash
    recurse(start_idx + idx + 1) # calculate children of row
    parent["children"].append(entry) # add row with children to row parent

    if indent < parent["indent"]:
    return

    stash.append(entry)

    recurse(0)


    This approach correctly appends child nodes to its parent, but fails to append children of children properly.

    I suspect there's a data structure approach that fits this problem neatly but I haven't found anything in my research.

    Any guidance is much appreciated!

    *I needed to manually add a style="padding-left: 0em" to the first non-header <td> of the .html for my index method to start from 0.

    Continue reading...

Compartilhe esta Página