2012-04-02 15 views
13

Tengo una tabla que debo analizar, específicamente es un horario escolar con 4 bloques de tiempo y 5 bloques de días para cada semana. Intenté analizarlo, pero honestamente no he llegado muy lejos porque estoy atascado con la forma de manejar los atributos rowspan y colspan, porque esencialmente significan que hay una falta de datos que necesito continuar.Analizando una tabla con rowspan y colspan

Como un ejemplo de lo que quiero hacer, aquí está una tabla:

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

quiero tomar esa mesa y convertirlo en esta lista:

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

En este momento estoy obtener una lista plana, similar a esto:

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

Pero en forma de diccionario, con información sobre cuántas columnas y ro ws se expande, una descripción de ello y en qué semana está.

Obviamente, esto debe funcionar para cada posibilidad de rowspan/colspan, y para varias semanas en la misma tabla.

El html no es tan limpio como lo he retratado, hay muchos atributos que he omitido, y el texto obviamente no está tan limpio como el 1,2,3,4, sino bloques de texto descriptivo. Pero si pudiera resolver esta parte, entonces debería ser lo suficientemente fácil de incorporar a lo que ya he escrito.

He estado usando lxml.html y Python para hacer esto, pero estoy abierto a usar otros módulos si proporciona una solución más fácil.

Espero que alguien pueda ayudarme, porque realmente no sé qué hacer.

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> 

Esto me está causando algunos problemas, esto está emitiendo

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

Con el código proporcionado por reclosedev, ¿qué necesito cambiar para adaptarla lo que da salida a

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

¿En lugar de eso?

Edit2: Utilizando la nueva función de reclosedev, se acerca a una solución, pero todavía hay casos en los que se produce un error de colocar las células correctamente:

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

Con esto, la tabla original muestra como tal:

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

Pero las nuevas salidas de llamadas esto:

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

Realmente nos ayudaría si pudiera mostrar lo que el código está utilizando actualmente y qué salida que está consiguiendo realmente, en lugar de un resultado similar a lo que está recibiendo. –

Respuesta

11

ACTUALIZACIÓN (función anterior eliminado)

Update2 fija y simplificada.

Mi primera función era incorrecto. Aquí hay otro, que está funcionando pero necesita pruebas:

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

Salida:

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

Tomó un poco para conseguir que trabajar en lo que tenía que hacer, específicamente necesitaba para dividir toda la tabla en tablas más pequeñas y eliminar algunas filas y columnas dentro de esas tablas, pero al final funcionó. Muchas gracias. – Atheuz

+0

reclosedev: He editado mi post principal, con un problema sobre cómo manejar un caso específico. Estaría agradecido si pudieras responderlo. – Atheuz

+0

@Atheuz, ver la respuesta actualizada. Primera función realmente estaba mal :(, pero éste debería funcionar. – reclosedev

1

Para aquellos que quieren un Python 3 y solución 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) 

Uso:

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

Ésta es NO optimizado. Escribí esto para mi guión de una sola vez.

Cuestiones relacionadas