I am trying to convert a BeautifulSoup4 HTML Table to a list of lists, iterating over each Tag elements and handling them accordingly.
I have an implementation of this that works at a surface level using BeautifulSoup4. However, the code is getting repetitive and complicated needlessly, but every time I try to improve it, I just end up breaking the functionality. I need some guidance on tidying this up.
Ultimately, I separate each type of HTML tags for any given row cell. The goal is to re-format the contents of the tables to an Excel spreadsheet and do partial cell formatting (still a work in progress, using xlwt
).
Note I've left out as much as possible of the parsing, but just enough to give an idea.
from bs4 import BeautifulSoup
from bs4.element import Tag, NavigableString
def handle_bs4_element(element):
if isinstance(element, Tag):
if len(element.contents) > 1:
# Handle each element separately and return a list? What if more elements are nested? Recursive call?
_res = []
for e_content in element.contents:
_res.append(handle_bs4_element(e_content))
if len(_res) == 1:
return _res[0]
else:
return _res
else:
tag_name = element.name
if tag_name == 'td':
_res = []
for td_content in element.contents:
_res.append(handle_bs4_element(td_content))
if len(_res) == 1:
return _res[0]
else:
return _res
elif tag_name in ('div', 'span'):
# This will probably contain more nested tags...
_res = []
for td_content in element.contents:
_res.append(handle_bs4_element(td_content))
if len(_res) == 1:
return _res[0]
else:
return _res
elif tag_name in ('p', 'strong', 'em', 'h3'): # Would handle each case separately, but just for the example
return element.text
elif tag_name == 'a':
e_text = element.text
e_link = element['href']
if e_text != e_link:
return '{text} ({url})'.format(text=e_text, url=e_link)
else:
return e_link
else:
print('Element HTML type not handled: {0}'.format(tag_name))
elif isinstance(element, NavigableString):
return element
else:
raise Exception('bs4 element of type {0} not handled...'.format(type(element)))
bs_table = BeautifulSoup(open('table_sample.html'), "html.parser")
headers = [h.text for h in bs_table.find_all('th')]
data = [headers]
rows = bs_table.find_all('tr')
for row in rows:
row_cells = row.find_all('td')
if row_cells:
# Handle each row cell appropriately
data.append([handle_bs4_element(rc) for rc in row_cells if handle_bs4_element(rc)])
print('\n'.join(map(str, data)))
table_sample.html:
<table class="confluenceTable">
<tbody>
<tr>
<th class="numberingColumn confluenceTh"> </th>
<th class="confluenceTh"><p><strong>Description</strong></p></th>
<th colspan="1" class="confluenceTh"><p>Col 1</p></th>
<th colspan="1" class="confluenceTh">Col 2</th>
<th colspan="1" class="confluenceTh">Col 3</th>
</tr>
<tr>
<td class="numberingColumn confluenceTd">1</td>
<td class="confluenceTd"><p>Some paragraph text</p></td>
<td colspan="1" class="confluenceTd">x</td>
<td colspan="1" class="confluenceTd">5</td>
<td colspan="1" class="confluenceTd">2</td>
</tr>
<tr>
<td class="numberingColumn confluenceTd">2</td>
<td colspan="4" class="confluenceTd"><h3 id="some-id1"><strong>HEADER 1</strong></h3></td>
</tr>
<tr>
<td class="numberingColumn confluenceTd">3</td>
<td colspan="1" class="confluenceTd"><div><p>Some text: </p><p>(1) Check out this <strong
style="line-height: 1.42857;">Figure 1.0.</strong></p></div></td>
<td colspan="1" class="confluenceTd">x</td>
<td colspan="1" class="confluenceTd">2</td>
<td colspan="1" class="confluenceTd">1</td>
</tr>
<tr>
<td class="numberingColumn confluenceTd">4</td>
<td colspan="1" class="confluenceTd"><p>(2) Some more text</p></td>
<td colspan="1" class="confluenceTd">x</td>
<td colspan="1" class="confluenceTd">2</td>
<td colspan="1" class="confluenceTd">1</td>
</tr>
<tr>
<td class="numberingColumn confluenceTd">5</td>
<td colspan="1" class="confluenceTd"><p>(3) Additional text</p></td>
<td colspan="1" class="confluenceTd">x</td>
<td colspan="1" class="confluenceTd">2</td>
<td colspan="1" class="confluenceTd">1</td>
</tr>
<tr>
<td class="numberingColumn confluenceTd">6</td>
<td colspan="1" class="confluenceTd"><p>(4) A bit more text</p></td>
<td colspan="1" class="confluenceTd">x</td>
<td colspan="1" class="confluenceTd">2</td>
<td colspan="1" class="confluenceTd">1</td>
</tr>
<tr>
<td class="numberingColumn confluenceTd">7</td>
<td colspan="1" class="confluenceTd"><span>(5) <span>A span <strong>Figure 1.0</strong> for
edited text. At this point the </span>span starts again</span></td>
<td colspan="1" class="confluenceTd">x</td>
<td colspan="1" class="confluenceTd">2</td>
<td colspan="1" class="confluenceTd">1</td>
</tr>
<tr>
<td class="numberingColumn confluenceTd">8</td>
<td colspan="4" class="confluenceTd"><h3 id="some-id2"><strong>HEADER 2</strong></h3></td>
</tr>
<tr>
<td class="numberingColumn confluenceTd">9</td>
<td colspan="1" class="confluenceTd"><p>Weird formatting, because Confluence</p><p> </p></td>
<td colspan="1" class="confluenceTd">x</td>
<td colspan="1" class="confluenceTd">4</td>
<td colspan="1" class="confluenceTd">2</td>
</tr>
<tr>
<td class="numberingColumn confluenceTd">10</td>
<td colspan="4" class="confluenceTd"><h3 id="some-id3"><strong>HEADER 3</strong></h3></td>
</tr>
<tr>
<td class="numberingColumn confluenceTd">11</td>
<td colspan="1" class="confluenceTd"><p>A paragraph about header 3.</p>
<div class="confluence-information-macro confluence-information-macro-information">
<span class="aui-icon aui-icon-small aui-iconfont-info confluence-information-macro-icon"></span>
<div class="confluence-information-macro-body">This is just silly. <strong>Strong</strong> indeed.</div>
</div>
</td>
<td colspan="1" class="confluenceTd">x</td>
<td colspan="1" class="confluenceTd">3</td>
<td colspan="1" class="confluenceTd">3</td>
</tr>
<tr>
<td class="numberingColumn confluenceTd">12</td>
<td colspan="1" class="confluenceTd"><span>Something about things or what not. Why is this in a span?</span></td>
<td colspan="1" class="confluenceTd">x</td>
<td colspan="1" class="confluenceTd">2</td>
<td colspan="1" class="confluenceTd">2</td>
</tr>
<tr>
<td class="numberingColumn confluenceTd">13</td>
<td colspan="4" class="confluenceTd"><h3 id="some-id4">HEADER 4</h3></td>
</tr>
<tr>
<td class="numberingColumn confluenceTd">14</td>
<td colspan="1" class="confluenceTd"><p>Section 4 baby! Or header.</p>
<div class="confluence-information-macro confluence-information-macro-information">
<span class="aui-icon aui-icon-small aui-iconfont-info confluence-information-macro-icon"></span>
<div class="confluence-information-macro-body">Confluence formatting fun.</div>
</div>
</td>
<td colspan="1" class="confluenceTd">x</td>
<td colspan="1" class="confluenceTd">2</td>
<td colspan="1" class="confluenceTd">3</td>
</tr>
<tr>
<td class="numberingColumn confluenceTd">15</td>
<td colspan="1" class="confluenceTd"><span>Pretty boring span of text</span></td>
<td colspan="1" class="confluenceTd">x</td>
<td colspan="1" class="confluenceTd">2</td>
<td colspan="1" class="confluenceTd">2</td>
</tr>
<tr>
<td class="numberingColumn confluenceTd">16</td>
<td colspan="4" class="confluenceTd"><h3 id="some-id5"><strong>HEADER 5</strong></h3></td>
</tr>
<tr>
<td class="numberingColumn confluenceTd">17</td>
<td colspan="1" class="confluenceTd"><p>A big paragraph describing more stuff. Super exciting.</p></td>
<td colspan="1" class="confluenceTd">x</td>
<td colspan="1" class="confluenceTd">4</td>
<td colspan="1" class="confluenceTd">2</td>
</tr>
</tbody>
</table>
Current output:
[u'\xa0', u'Description', u'Col 1', u'Col 2', u'Col 3']
[u'1', u'Some paragraph text', u'x', u'5', u'2']
[u'2', u'HEADER 1']
[u'3', [u'Some text: ', [u'(1) Check out this ', u'Figure 1.0.']], u'x', u'2', u'1']
[u'4', u'(2)\xa0\xa0\xa0Some more text', u'x', u'2', u'1']
[u'5', u'(3)\xa0\xa0\xa0 Additional text', u'x', u'2', u'1']
[u'6', u'(4)\xa0\xa0\xa0 A bit more text', u'x', u'2', u'1']
[u'7', [u'(5)\xa0\xa0\xa0 ', [u'A span ', u'Figure 1.0', u' for\n edited text. At this point the '], u'span starts again'], u'x', u'2', u'1']
[u'8', u'HEADER 2']
[u'9', [u'Weird formatting, because Confluence', u'\xa0'], u'x', u'4', u'2']
[u'10', u'HEADER 3']
[u'11', [u'A paragraph about header 3.', u'\n', [u'\n', [], u'\n', [u'This is just silly. ', u'Strong', u' indeed.'], u'\n'], u'\n'], u'x', u'3', u'3']
[u'12', u'Something about things or what not. Why is this in a span?', u'x', u'2', u'2']
[u'13', u'HEADER 4']
[u'14', [u'Section 4 baby! Or header.', u'\n', [u'\n', [], u'\n', u'Confluence formatting fun.', u'\n'], u'\n'], u'x', u'2', u'3']
[u'15', u'Pretty boring span of text', u'x', u'2', u'2']
[u'16', u'HEADER 5']
[u'17', u'A big paragraph describing more stuff. Super exciting.', u'x', u'4', u'2']