Разбор стола с рядами и колпаном
У меня есть таблица, которую мне нужно разобрать, в частности, это школьный график с 4 блоками времени и 5 блоков дней в неделю. Я попытался разобрать его, но, честно говоря, не очень далеко, потому что я застрял в том, что касается атрибутов rowspan и colspan, потому что они по существу означают отсутствие данных, которые мне нужно продолжить.
В качестве примера того, что я хочу сделать, вот таблица:
<tr>
<td colspan="2" rowspan="4">#1</td>
<td rowspan="4">#2</td>
<td rowspan="2">#3</td>
<td rowspan="2">#4</td>
</tr>
<tr>
</tr>
<tr>
<td rowspan="2">#5</td>
<td rowspan="2">#6</td>
</tr>
<tr>
</tr>
Я хочу взять эту таблицу и преобразовать ее в этот список:
[[1,1,2,3,4],
[1,1,2,3,4],
[1,1,2,5,6],
[1,1,2,5,6]]
Сейчас я получаю плоский список, похожий на это:
[1,2,3,4,5,6]
Но в словарной форме, с информацией о том, сколько столбцов и строк она охватывает, описание ее и на какой неделе она.
Очевидно, что это должно работать для каждой возможности rowspan/colspan и в течение нескольких недель в одной таблице.
html не так чист, как я его изобразил, есть много атрибутов, которые я забыл, и текст явно не такой чистый, как 1,2,3,4, а скорее блок описательных текст. Но если бы я смог решить эту часть, то ее было бы достаточно легко включить в то, что я уже написал.
Я использовал lxml.html и Python для этого, но я открыт для использования других модулей, если он обеспечивает более легкое решение.
Я надеюсь, что кто-то может мне помочь, потому что я действительно не знаю, что делать.
EDIT:
<table>
<tr>
<td> </td>
<td> </td>
<td> </td>
<td rowspan="4">Thing</td>
<td> </td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
</tr>
</table>
Это вызывает некоторые проблемы, это выводит
[' ', ' ', ' ', 'Thing', ' ']
[' ', ' ', ' ', ' ', ' ']
[' ', ' ', ' ', ' ', ' ']
[' ', ' ', ' ', ' ', ' ']
С кодом, предоставленным reclosedev, что мне нужно изменить для его адаптации, чтобы он выводил
[' ', ' ', ' ', 'Thing', ' ']
[' ', ' ', ' ', 'Thing', ' ']
[' ', ' ', ' ', 'Thing', ' ']
[' ', ' ', ' ', 'Thing', ' ']
Вместо
EDIT2: Используя новую функцию reclosedev, она приближается к решению, но все еще есть случаи, когда он не может правильно помещать ячейки:
<table>
<tr>
<td> </td>
<td rowspan="2"> DMAT Aud. 6 </td>
<td rowspan="4"> Exam</td>
<td rowspan="2"> DMAT Aud. 7</td>
<td> </td>
</tr>
<tr>
<td> </td>
<td rowspan="2"> CART Aud. 4</td>
</tr>
<tr>
<td> </td>
<td rowspan="2"> CART Aud. 4</td>
<td rowspan="2"> OOP Aud. 7</td>
</tr>
<tr>
<td> </td>
<td> </td>
</tr>
</table>
При этом исходная таблица показывает это как таковое:
[
[' ', ' DMAT Aud. 6 ', ' Exam', ' DMAT Aud. 7', ' '],
[' ', ' DMAT Aud. 6 ', ' Exam', ' DMAT Aud. 7', ' CART Aud. 4'],
[' ', ' CART Aud. 4' , ' Exam', ' OOP Aud. 7' , ' CART Aud. 4'],
[' ', ' CART Aud. 4' , ' Exam', ' OOP Aud. 7' , ' ']
]
Но новый вызов выводит это:
[
[' ', ' DMAT Aud. 6 ', ' Exam', ' DMAT Aud. 7', ' '],
[' ', ' DMAT Aud. 6 ', ' Exam', ' DMAT Aud. 7', ' CART Aud. 4'],
[' ', ' CART Aud. 4' , ' Exam', ' CART Aud. 4', ' OOP Aud. 7'],
[' ', ' CART Aud. 4' , ' Exam', ' OOP Aud. 7' , ' ']
]
Ответы
Ответ 1
UPDATE (удалена предыдущая функция)
UPDATE2 исправлено и упрощено.
Моя первая функция была неправильной. Здесь еще один, он работает, но нуждается в тестах:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from collections import defaultdict
def table_to_list(table):
dct = table_to_2d_dict(table)
return list(iter_2d_dict(dct))
def table_to_2d_dict(table):
result = defaultdict(lambda : defaultdict(unicode))
for row_i, row in enumerate(table.xpath('./tr')):
for col_i, col in enumerate(row.xpath('./td|./th')):
colspan = int(col.get('colspan', 1))
rowspan = int(col.get('rowspan', 1))
col_data = col.text_content()
while row_i in result and col_i in result[row_i]:
col_i += 1
for i in range(row_i, row_i + rowspan):
for j in range(col_i, col_i + colspan):
result[i][j] = col_data
return result
def iter_2d_dict(dct):
for i, row in sorted(dct.items()):
cols = []
for j, col in sorted(row.items()):
cols.append(col)
yield cols
if __name__ == '__main__':
import lxml.html
from pprint import pprint
doc = lxml.html.parse('tables.html')
for table_el in doc.xpath('//table'):
table = table_to_list(table_el)
pprint(table)
tables.html
<table border="1">
<tr>
<td>1 </td>
<td>1 </td>
<td>1 </td>
<td rowspan="4">Thing</td>
<td>1 </td>
</tr>
<tr>
<td>2 </td>
<td>2 </td>
<td>2 </td>
<td>2 </td>
</tr>
<tr>
<td>3 </td>
<td>3 </td>
<td>3 </td>
<td>3 </td>
</tr>
<tr>
<td>4 </td>
<td>4 </td>
<td>4 </td>
<td>4 </td>
</tr>
</table>
<table border="1">
<tr>
<td colspan="2" rowspan="4">#1</td>
<td rowspan="4">#2</td>
<td rowspan="2">#3</td>
<td rowspan="2">#4</td>
</tr>
<tr></tr>
<tr>
<td rowspan="2">#5</td>
<td rowspan="2">#6</td>
</tr>
<tr></tr>
</table>
Вывод:
[['1 ', '1 ', '1 ', 'Thing', '1 '],
['2 ', '2 ', '2 ', 'Thing', '2 '],
['3 ', '3 ', '3 ', 'Thing', '3 '],
['4 ', '4 ', '4 ', 'Thing', '4 ']]
[['#1', '#1', '#2', '#3', '#4'],
['#1', '#1', '#2', '#3', '#4'],
['#1', '#1', '#2', '#5', '#6'],
['#1', '#1', '#2', '#5', '#6']]
Ответ 2
Для тех, кто хочет решение Python 3 и BeautifulSoup,
def table_to_2d(table_tag):
rows = table_tag("tr")
cols = rows[0](["td", "th"])
table = [[None] * len(cols) for _ in range(len(rows))]
for row_i, row in enumerate(rows):
for col_i, col in enumerate(row(["td", "th"])):
insert(table, row_i, col_i, col)
return table
def insert(table, row, col, element):
if row >= len(table) or col >= len(table[row]):
return
if table[row][col] is None:
value = element.get_text()
table[row][col] = value
if element.has_attr("colspan"):
span = int(element["colspan"])
for i in range(1, span):
table[row][col+i] = value
if element.has_attr("rowspan"):
span = int(element["rowspan"])
for i in range(1, span):
table[row+i][col] = value
else:
insert(table, row, col + 1, element)
Использование:
soup = BeautifulSoup('<table><tr><th>1</th><th>2</th><th>5</th></tr><tr><td rowspan="2">3</td><td colspan="2">4</td></tr><tr><td>6</td><td>7</td></tr></table>', 'html.parser')
print(table_to_2d(soup.table))
Это НЕ оптимизировано. Я написал это для своего одноразового script.