Предыстория: мне нужно написать анализатор html-таблиц на python для HTML-таблиц с различными colspan и rowspan. После некоторых исследований я наткнулся на этот драгоценный камень. Это хорошо работает для простых случаев без дурацких colspan и rowspan, однако я столкнулся с ошибкой. В коде предполагается, что если элемент имеет colspan, равный 3, он принадлежит трем разным заголовкам таблицы, в то время как на самом деле он принадлежит только тому заголовку таблицы, в центре которого находится colspan. Пример этого можно увидеть на странице http://en.wiktionary.org/wiki/han#Swedish (откройте таблицу склонений в разделе Шведский). Код неправильно возвращает, что "hans" (притяжательный средний род мужского рода от 3-го лица) принадлежит мужскому роду притяжательного-общего-3-го лица и мужскому роду притяжательно-множественного числа от 3-го лица, потому что он имеет colspan 3. Я попытался добавить проверку в table_to_2d_dict, который создаст счетчик, если colspan > 1
, и только подсчитает элемент как часть заголовка, если счетчик был равен colspan // 2 + 1
(это возвращает медиану диапазона (1, colspan+1), который является значением заголовок таблицы, за который следует считать элемент). Однако, когда я реализую эту проверку в месте, указанном в приведенном ниже коде, она не работает. Честно говоря, это, вероятно, связано с моим непониманием того, как работает этот код, так что...
Вопрос. Кто-нибудь может объяснить, что делает этот код и почему он работает неправильно, как описано выше? Если кто-то может реализовать исправление, это было бы здорово, но сейчас меня в первую очередь интересует понимание кода. Спасибо
Ниже приведен код с комментариями, которые я добавил, чтобы выделить части кода, которые я понимаю, и части, которые мне непонятны.
#!/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(str))
for row_i, row in enumerate(table.xpath('./tr')): #these double for loops iterate over each element in the table
for col_i, col in enumerate(row.xpath('./td|./th')):
colspan = int(col.get('colspan', 1)) #gets colspan attr of the element, if none assumes it's 1
rowspan = int(col.get('rowspan', 1)) #gets rowspan attr of the element, if none assumes it's 1
col_data = col.text_content() #gets raw text inside element
#WHAT DOES THIS DO? :(
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
#what does this do? :(
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)