2012-04-02 13 views
13

Mam tabelę, którą muszę przeanalizować, w szczególności jest to plan zajęć w szkole z 4 blokami czasu i 5 bloków dni na każdy tydzień. Próbowałem to przeanalizować, ale szczerze mówiąc, nie dotarłem zbyt daleko, ponieważ utknąłem w kwestii radzenia sobie z atrybutami rowspan i colspan, ponieważ zasadniczo oznaczają one brak danych, które muszę kontynuować.Parsowanie tabeli z odstępem między wierszami i kolspanem

Jako przykład tego, co chcę zrobić, oto tabela:

<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> 

chcę wziąć ten stół i przekształcić go w tej liście:

[[1,1,2,3,4], 
[1,1,2,3,4], 
[1,1,2,5,6], 
[1,1,2,5,6]] 

Teraz jestem uzyskanie płaskiej listy, podobnego do tego:

[1,2,3,4,5,6] 

ale w formie słownika, z informacji na temat jak wiele kolumn i ro ws to rozciąga się, opis go i jaki jest tydzień.

Oczywiście to musi działać dla każdej możliwości rowspan/colspan i przez wiele tygodni w tej samej tabeli.

HTML nie jest tak czysty, jak go przedstawiłem, jest wiele atrybutów, które pominąłem, a tekst nie jest oczywiście tak czysty jak 1,2,3,4, ale raczej blokuje opisowy tekst. Ale jeśli uda mi się rozwiązać tę część, to powinno być to łatwe do włączenia do tego, co już napisałem.

Używam lxml.html i Python, aby to zrobić, ale jestem otwarty na używanie innych modułów, jeśli zapewnia to łatwiejsze rozwiązanie.

Mam nadzieję, że ktoś może mi pomóc, bo naprawdę nie wiem, co robić.

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> 

To jest przyczyną mnie jakieś problemy, to jest wyprowadzanie

[' ', ' ', ' ', 'Thing', ' '] 
[' ', ' ', ' ', ' ', ' '] 
[' ', ' ', ' ', ' ', ' '] 
[' ', ' ', ' ', ' ', ' '] 

z kodem dostarczonych przez reclosedev, co muszę zmienić, aby je dostosować tak wyprowadza

[' ', ' ', ' ', 'Thing', ' '] 
[' ', ' ', ' ', 'Thing', ' '] 
[' ', ' ', ' ', 'Thing', ' '] 
[' ', ' ', ' ', 'Thing', ' '] 

Zamiast tego?

EDIT2: Korzystanie z nowej funkcji reclosedev jest, to zbliża się rozwiązanie, ale nadal istnieją przypadki, w których nie udaje mu się umieścić komórki poprawnie:

<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> 

Z tym, oryginalna tabela pokazuje go jako takie:

[ 
[' ', ' 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' , ' '] 
] 

Ale nowe wyjścia nazywają to:

[ 
[' ', ' 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' , ' '] 
] 
+3

Naprawdę by nam to pomogło, gdybyś mógł pokazać nam, jakiego kodu obecnie używasz i jaki jest wynik, zamiast wyjść, który jest podobny do tego, co dostajesz. –

Odpowiedz

11

UPDATE (usunięcie poprzedniej funkcji)

Update2 stałe i uproszczone.

Moja pierwsza funkcja była nieprawidłowa. Oto kolejny, że to działa, ale potrzebuje testy:

#!/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> 

wyjściowa:

[['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']] 
+0

Trochę zajęło mi przygotowanie go do pracy nad tym, co było mi potrzebne, w szczególności musiałem podzielić cały stół na mniejsze tabele i usunąć niektóre wiersze i kolumny wewnątrz tych tabel, ale w końcu zadziałało. Dziękuję bardzo. – Atheuz

+0

reclosedev: Edytowałem mój główny post, z problemem, jak poradzić sobie w konkretnym przypadku. Byłbym wdzięczny, gdybyś mógł na nie odpowiedzieć. – Atheuz

+0

@Atheuz, patrz zaktualizowana odpowiedź. Pierwsza funkcja była naprawdę nieprawidłowa :(, ale ta powinna zadziałać – reclosedev

1

Dla tych, którzy chcą Python 3 i rozwiązanie 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) 

Zastosowanie:

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)) 

To NIE zoptymalizowane. Napisałem to dla mojego jednorazowego scenariusza.

Powiązane problemy