Как преобразовать таблицу HTML в массив в python

У меня есть html-документ, и я хочу вытащить таблицы из этого документа и вернуть их как массивы. Я представляю две функции, которые обнаруживают все html-таблицы в документе, а вторую, которые превращают html-таблицы в двумерные массивы.

Что-то вроде этого:

htmltables = get_tables(htmldocument)
for table in htmltables:
    array=make_array(table)

Там 2 уловы: 1. Таблицы номеров меняются день ото дня 2. В таблицах есть все виды сверхъестественного форматирования, например, смелые и мигающие теги, случайным образом вбрасываемые.

Спасибо!

Ответ 1

Pandas может извлекать все таблицы в вашем html в список фреймов данных прямо из коробки, что избавляет вас от необходимости самостоятельно проанализировать страницу (изобретать колесо). A DataFrame является мощным типом двумерного массива.

Я рекомендую продолжать работать с данными через Pandas, так как это отличный инструмент, но вы также можете конвертировать в другие форматы (список, словарь, файл csv и т.д.).

Пример

"""Extract all tables from an html file, printing and saving each to csv file."""

import pandas as pd

df_list = pd.read_html('my_file.html')

for i, df in enumerate(df_list):
    print df
    df.to_csv('table {}.csv'.format(i))

Получение содержимого html непосредственно из Интернета, а не из файла, потребует лишь незначительной модификации:

import requests

html = requests.get('my_url').content
df_list = pd.read_html(html)

Ответ 2

Используйте BeautifulSoup (рекомендую 3.0.8). Поиск всех таблиц тривиален:

import BeautifulSoup

def get_tables(htmldoc):
    soup = BeautifulSoup.BeautifulSoup(htmldoc)
    return soup.findAll('table')

Однако, в Python, array является одномерным и ограничен хорошими элементарными типами как элементы (целые числа, поплавки, эти элементарные). Таким образом, нет способа сжать таблицу HTML в Python array.

Может быть, вы имеете в виду Python list вместо этого? Это также одномерное, но все может быть элементом, поэтому у вас может быть список списков (по моему мнению, один подсчет за тег tr, содержащий один элемент в теге td).

Это даст:

def makelist(table):
  result = []
  allrows = table.findAll('tr')
  for row in allrows:
    result.append([])
    allcols = row.findAll('td')
    for col in allcols:
      thestrings = [unicode(s) for s in col.findAll(text=True)]
      thetext = ''.join(thestrings)
      result[-1].append(thetext)
  return result

Это может быть совсем не совсем то, что вы хотите (не пропускает HTML-комментарии, элементы подписок - строки юникода, а не байтовые строки и т.д.), но его нужно легко настроить.

Ответ 3

A +1 для вопросителя, а другой - к богу Питона.
Хотелось попробовать этот пример с помощью селекторов lxml и CSS.
Да, это в основном то же, что и пример Alex:

import lxml.html
markup = lxml.html.fromstring('''<html><body>\
<table width="600">
    <tr>
        <td width="50%">0,0,0</td>
        <td width="50%">0,0,1</td>
    </tr>
    <tr>
        <td>0,1,0</td>
        <td>0,1,1</td>
    </tr>
</table>
<table>
    <tr>
        <td>1,0,0</td>
        <td>1,<blink>0,</blink>1</td>
        <td>1,0,2</td>
        <td><bold>1</bold>,0,3</td>
    </tr>
</table>
</body></html>''')

tbl = []
rows = markup.cssselect("tr")
for row in rows:
  tbl.append(list())
  for td in row.cssselect("td"):
    tbl[-1].append(unicode(td.text_content()))

pprint(tbl)
#[[u'0,0,0', u'0,0,1'],
# [u'0,1,0', u'0,1,1'],
# [u'1,0,0', u'1,0,1', u'1,0,2', u'1,0,3']]