Difficulty with find() function in Beautiful Soup

Viewed 54

I am trying to extract a value from a web page, and I'm having a little bit of trouble. The HTML looks like:

<table class="sal-sector-exposure__sector-table"><thead><tr><th>Sectors</th> <th><span class="sal-sector-exposure__col-icon sal-sector-exposure__col-icon--fund"></span> <span class="sal-sector-exposure__col-title">Fund</span> <span class="sal-sector-exposure__col-pct">%</span></th> <!----> <th><span class="sal-sector-exposure__col-icon sal-sector-exposure__col-icon--benchmark"></span> <span class="sal-sector-exposure__col-title sal-sector-exposure__col-title--long">Category</span> <span class="sal-sector-exposure__col-title sal-sector-exposure__col-title--short">Cat</span> <span class="sal-sector-exposure__col-pct">%</span></th></tr></thead> <tbody><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-basic-materials"></i></span> <span>Basic Materials</span></td> <td><span>5.46</span></td> <!----> <td><span>4.60</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-consumer-cyclical"></i></span> <span>Consumer Cyclical</span></td> <td><span>11.95</span></td> <!----> <td><span>12.04</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-financial-services"></i></span> <span>Financial Services</span></td> <td><span>17.97</span></td> <!----> <td><span>15.44</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-real-estate"></i></span> <span>Real Estate</span></td> <td><span>1.86</span></td> <!----> <td><span>5.16</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-communication-services"></i></span> <span>Communication Services</span></td> <td><span>8.61</span></td> <!----> <td><span>9.22</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-energy"></i></span> <span>Energy</span></td> <td><span>4.15</span></td> <!----> <td><span>3.14</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-industrials"></i></span> <span>Industrials</span></td> <td><span>12.08</span></td> <!----> <td><span>10.62</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-technology"></i></span> <span>Technology</span></td> <td><span>18.32</span></td> <!----> <td><span>19.33</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-consumer-defensive"></i></span> <span>Consumer Defensive</span></td> <td><span>5.25</span></td> <!----> <td><span>5.96</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-healthcare"></i></span> <span>Healthcare</span></td> <td><span>12.38</span></td> <!----> <td><span>12.03</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-utilities"></i></span> <span>Utilities</span></td> <td><span>1.96</span></td> <!----> <td><span>2.45</span></td></tr></tbody></table>

I want to extract the 11.95 in bold using beautiful soup, but I'm not sure how. It's only embedded in two span tags, and I don't know how to get more specific. Please help!

3 Answers

Try this:

from bs4 import BeautifulSoup

html = """<table class="sal-sector-exposure__sector-table"><thead><tr><th>Sectors</th> <th><span class="sal-sector-exposure__col-icon sal-sector-exposure__col-icon--fund"></span> <span class="sal-sector-exposure__col-title">Fund</span> <span class="sal-sector-exposure__col-pct">%</span></th> <!----> <th><span class="sal-sector-exposure__col-icon sal-sector-exposure__col-icon--benchmark"></span> <span class="sal-sector-exposure__col-title sal-sector-exposure__col-title--long">Category</span> <span class="sal-sector-exposure__col-title sal-sector-exposure__col-title--short">Cat</span> <span class="sal-sector-exposure__col-pct">%</span></th></tr></thead> <tbody><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-basic-materials"></i></span> <span>Basic Materials</span></td> <td><span>5.46</span></td> <!----> <td><span>4.60</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-consumer-cyclical"></i></span> <span>Consumer Cyclical</span></td> <td><span>11.95</span></td> <!----> <td><span>12.04</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-financial-services"></i></span> <span>Financial Services</span></td> <td><span>17.97</span></td> <!----> <td><span>15.44</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-real-estate"></i></span> <span>Real Estate</span></td> <td><span>1.86</span></td> <!----> <td><span>5.16</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-communication-services"></i></span> <span>Communication Services</span></td> <td><span>8.61</span></td> <!----> <td><span>9.22</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-energy"></i></span> <span>Energy</span></td> <td><span>4.15</span></td> <!----> <td><span>3.14</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-industrials"></i></span> <span>Industrials</span></td> <td><span>12.08</span></td> <!----> <td><span>10.62</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-technology"></i></span> <span>Technology</span></td> <td><span>18.32</span></td> <!----> <td><span>19.33</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-consumer-defensive"></i></span> <span>Consumer Defensive</span></td> <td><span>5.25</span></td> <!----> <td><span>5.96</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-healthcare"></i></span> <span>Healthcare</span></td> <td><span>12.38</span></td> <!----> <td><span>12.03</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-utilities"></i></span> <span>Utilities</span></td> <td><span>1.96</span></td> <!----> <td><span>2.45</span></td></tr></tbody></table>
"""
css_class_selector = "ms-sector-consumer-cyclical"

soup = BeautifulSoup(html, 'lxml')
span_child = soup.find("i", {"class":css_class_selector})
target_row = span_child.parent.parent.parent
target_value = target_row.find_all("td")[1].text
print(target_value)

I've included the html you've provided as a string-variable in order to test my solution

You can use CSS selector that finds <td> containing string "Consumer Cyclical" and then next <td> sibling:

from bs4 import BeautifulSoup

html_doc = """<table class="sal-sector-exposure__sector-table"><thead><tr><th>Sectors</th> <th><span class="sal-sector-exposure__col-icon sal-sector-exposure__col-icon--fund"></span> <span class="sal-sector-exposure__col-title">Fund</span> <span class="sal-sector-exposure__col-pct">%</span></th> <!----> <th><span class="sal-sector-exposure__col-icon sal-sector-exposure__col-icon--benchmark"></span> <span class="sal-sector-exposure__col-title sal-sector-exposure__col-title--long">Category</span> <span class="sal-sector-exposure__col-title sal-sector-exposure__col-title--short">Cat</span> <span class="sal-sector-exposure__col-pct">%</span></th></tr></thead> <tbody><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-basic-materials"></i></span> <span>Basic Materials</span></td> <td><span>5.46</span></td> <!----> <td><span>4.60</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-consumer-cyclical"></i></span> <span>Consumer Cyclical</span></td> <td><span>11.95</span></td> <!----> <td><span>12.04</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-financial-services"></i></span> <span>Financial Services</span></td> <td><span>17.97</span></td> <!----> <td><span>15.44</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-real-estate"></i></span> <span>Real Estate</span></td> <td><span>1.86</span></td> <!----> <td><span>5.16</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-communication-services"></i></span> <span>Communication Services</span></td> <td><span>8.61</span></td> <!----> <td><span>9.22</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-energy"></i></span> <span>Energy</span></td> <td><span>4.15</span></td> <!----> <td><span>3.14</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-industrials"></i></span> <span>Industrials</span></td> <td><span>12.08</span></td> <!----> <td><span>10.62</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-technology"></i></span> <span>Technology</span></td> <td><span>18.32</span></td> <!----> <td><span>19.33</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-consumer-defensive"></i></span> <span>Consumer Defensive</span></td> <td><span>5.25</span></td> <!----> <td><span>5.96</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-healthcare"></i></span> <span>Healthcare</span></td> <td><span>12.38</span></td> <!----> <td><span>12.03</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-utilities"></i></span> <span>Utilities</span></td> <td><span>1.96</span></td> <!----> <td><span>2.45</span></td></tr></tbody></table>"""
soup = BeautifulSoup(html_doc, "html.parser")

val = soup.select_one('td:-soup-contains("Consumer Cyclical") + td')
print(val.get_text(strip=True))

Prints:

11.95

Or find class="ms-sector-consumer-cyclical" and then next <td>:

val = soup.select_one(".ms-sector-consumer-cyclical").find_next("td")
print(val.get_text(strip=True))

EDIT: To load the data from API:

import json
import requests


url = "https://api-global.morningstar.com/sal-service/v1/fund/portfolio/v2/sector/F00000MJ3L/data?languageId=en&locale=en&clientId=MDC&benchmarkId=category&component=sal-components-mip-sector-exposure&version=3.49.0"
headers = {"ApiKey": "lstzFDEOhfFNMLikKa0am9mgEKLBl49T"}

data = requests.get(url, headers=headers).json()

# uncomment this to print all data:
# print(json.dumps(data, indent=4))

print(data["EQUITY"]["fundPortfolio"]["consumerCyclical"])

Prints:

11.95041

There are many ways to do this. Here's another one that does not rely on BeautifulSoup. It's very specific to your case.

from lxml import etree
html ='<table class="sal-sector-exposure__sector-table"><thead><tr><th>Sectors</th> <th><span class="sal-sector-exposure__col-icon sal-sector-exposure__col-icon--fund"></span> <span class="sal-sector-exposure__col-title">Fund</span> <span class="sal-sector-exposure__col-pct">%</span></th> <!----> <th><span class="sal-sector-exposure__col-icon sal-sector-exposure__col-icon--benchmark"></span> <span class="sal-sector-exposure__col-title sal-sector-exposure__col-title--long">Category</span> <span class="sal-sector-exposure__col-title sal-sector-exposure__col-title--short">Cat</span> <span class="sal-sector-exposure__col-pct">%</span></th></tr></thead> <tbody><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-basic-materials"></i></span> <span>Basic Materials</span></td> <td><span>5.46</span></td> <!----> <td><span>4.60</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-consumer-cyclical"></i></span> <span>Consumer Cyclical</span></td> <td><span>11.95</span></td> <!----> <td><span>12.04</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-financial-services"></i></span> <span>Financial Services</span></td> <td><span>17.97</span></td> <!----> <td><span>15.44</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-real-estate"></i></span> <span>Real Estate</span></td> <td><span>1.86</span></td> <!----> <td><span>5.16</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-communication-services"></i></span> <span>Communication Services</span></td> <td><span>8.61</span></td> <!----> <td><span>9.22</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-energy"></i></span> <span>Energy</span></td> <td><span>4.15</span></td> <!----> <td><span>3.14</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-industrials"></i></span> <span>Industrials</span></td> <td><span>12.08</span></td> <!----> <td><span>10.62</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-technology"></i></span> <span>Technology</span></td> <td><span>18.32</span></td> <!----> <td><span>19.33</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-consumer-defensive"></i></span> <span>Consumer Defensive</span></td> <td><span>5.25</span></td> <!----> <td><span>5.96</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-healthcare"></i></span> <span>Healthcare</span></td> <td><span>12.38</span></td> <!----> <td><span>12.03</span></td></tr><tr><td><span class="sal-sector-exposure__sector"><i class="ms ms-sector-utilities"></i></span> <span>Utilities</span></td> <td><span>1.96</span></td> <!----> <td><span>2.45</span></td></tr></tbody></table>'
dom = etree.HTML(html)
x = dom.xpath('//table[contains(@class, "sal-sector-exposure__sector-table")]/tbody/tr[2]/td[2]/span')
print(x[0].text)
Related