A table is rows of cells. Skip the header row, then read <td> text. select("table#sales tbody tr") is more precise than every tr on the page.
Goal
Parse a city × units table into a list of lists.
Rows
html = """
<table id="sales">
<thead>
<tr><th>city</th><th>units</th></tr>
</thead>
<tbody>
<tr><td>Nairobi</td><td>12</td></tr>
<tr><td>Mombasa</td><td>9</td></tr>
<tr><td>Kisumu</td><td>11</td></tr>
</tbody>
</table>
"""
soup = BeautifulSoup(html, "html.parser")
rows = []
for tr in soup.select("#sales tbody tr"):
rows.append([td.get_text(strip=True) for td in tr.find_all("td")])
print(rows)Header names
html = """
<table>
<tr><th>city</th><th>product</th><th>units</th></tr>
<tr><td>Nairobi</td><td>A</td><td>12</td></tr>
<tr><td>Kisumu</td><td>B</td><td>11</td></tr>
</table>
"""
soup = BeautifulSoup(html, "html.parser")
header = [th.get_text(strip=True) for th in soup.select("tr")[0].find_all("th")]
body = []
for tr in soup.select("tr")[1:]:
body.append([td.get_text(strip=True) for td in tr.find_all("td")])
print(header)
print(body)Pitfall
find_all("tr") also hits header rows. Either use tbody or skip index 0.