2013-06-23 4 views
5

Я пытаюсь использовать BeautifulSoup для анализа таблицы html, которую я загрузил в http://pastie.org/8070879, чтобы получить три столбца (от 0 до 735, от 0.50 до 1.0 и от 0.5 до 0.0) в виде списков. Чтобы объяснить, почему, я хочу, чтобы целые числа 0-735 были ключами, а десятичные числа - значениями.Parse HTML Table with Python BeautifulSoup

Из чтения многих других сообщений на SO, я придумал следующее, которое не подходит для создания списков, которые я хочу. Все это делает отображать текст в таблице, как видно здесь http://i1285.photobucket.com/albums/a592/TheNexulo/output_zps20c5afb8.png

from bs4 import BeautifulSoup 

soup = BeautifulSoup(open("fide.html")) 
table = soup.find('table') 

rows = table.findAll('tr') 

for tr in rows: 
    cols = tr.findAll('td') 
    for td in cols: 
    text = ''.join(td.find(text=True)) 
    print text + "|", 
    print 

Я новичок в Python и BeautifulSoup, поэтому, пожалуйста, быть нежным со мной! Спасибо

+0

Загрузить картину того, как вы хотите, чтобы данные, которые будут представлены в конце концов. +1 для шахматной проблемы. – marlenunez

+0

Он отображает текст в таблице, потому что это то, что делает ваш код. Почему бы вам не нажимать каждое поле в словарь, где ключ является вашим целым числом, а список десятичных знаков - значением? – Blender

ответ

3

HTML-парсеры, такие как BeautifulSoup, предполагают, что вы хотите, это объектная модель, которая отражает входную структуру HTML. Но иногда (как в этом случае) эта модель оказывается на пути больше, чем помогает. Pyparsing включает некоторые функции синтаксического анализа HTML, которые более надежны, чем просто использование сырых регулярных выражений, но в противном случае работают аналогичным образом, позволяя вам определять фрагменты HTML-интереса и просто игнорировать остальные. Вот парсер, который читает Опубликованная источник HTML:

from pyparsing import makeHTMLTags,withAttribute,Suppress,Regex,Group 

""" looking for this recurring pattern: 
      <td valign="top" bgcolor="#FFFFCC">00-03</td> 
      <td valign="top">.50</td> 
      <td valign="top">.50</td> 

    and want a dict with keys 0, 1, 2, and 3 all with values (.50,.50) 
""" 

td,tdend = makeHTMLTags("td") 
keytd = td.copy().setParseAction(withAttribute(bgcolor="#FFFFCC")) 
td,tdend,keytd = map(Suppress,(td,tdend,keytd)) 

realnum = Regex(r'1?\.\d+').setParseAction(lambda t:float(t[0])) 
integer = Regex(r'\d{1,3}').setParseAction(lambda t:int(t[0])) 
DASH = Suppress('-') 

# build up an expression matching the HTML bits above 
entryExpr = (keytd + integer("start") + DASH + integer("end") + tdend + 
        Group(2*(td + realnum + tdend))("vals")) 

Этот анализатор не только выбирает подходящие троек, он также извлекает старт класса целых и пар действительных чисел (а также уже преобразующий из строки к целым числам или поплавкам во время разбора).

Глядя на стол, я предполагаю, что вам действительно нужен поиск, который займет такой ключ, как 700, и вернет пару значений (0,99, 0,01), так как 700 попадает в диапазон 620-735. Этот бит кода ищет исходный HTML-текст, перебирает соответствующих записей и вставляет пар ключ-значение в Словаре поиска:

# search the input HTML for matches to the entryExpr expression, and build up lookup dict 
lookup = {} 
for entry in entryExpr.searchString(sourcehtml): 
    for i in range(entry.start, entry.end+1): 
     lookup[i] = tuple(entry.vals) 

А теперь попробовать несколько подстановочных:

# print out some test values 
for test in (0,20,100,700): 
    print (test, lookup[test]) 

печатает:

0 (0.5, 0.5) 
20 (0.53, 0.47) 
100 (0.64, 0.36) 
700 (0.99, 0.01) 
+0

Большое вам спасибо! Я просто просматриваю это, чтобы убедиться, что я понимаю, и тогда я пометю его как принятый ответ. –

+0

Когда я запускаю код, я получаю KeyError: 0 в строке, где я пытаюсь напечатать некоторые тестовые значения, что предполагает, что ключ, который я ищу, не в словаре? –

+0

Если вы получаете ошибки, тогда начинайте работать в обратном направлении. Посмотрите, что такое содержимое поиска. Если поиск пуст, то, возможно, entryExpr ничего не соответствует. Если entryExpr ничего не сопоставлял, возможно, текст, который вы обрабатываете, не соответствует примеру, который вы опубликовали. – PaulMcG

3

Я думаю, что выше ответ лучше, чем то, что я хотел бы предложить, но у меня есть ответ BeautifulSoup, который может получить вы начали. Это немного хаки, но я подумал, что предлагаю его все же.

С BeautifulSoup, вы можете найти все тег с определенными атрибутами следующим образом (предполагается, что у вас есть soup.object уже создан):

soup.find_all('td', attrs={'bgcolor':'#FFFFCC'}) 

Это будет найти все ваши ключи. Трюк состоит в том, чтобы связать их со значениями, которые вы хотите, которые все появляются сразу же и которые попарно (если эти вещи меняются, кстати, это решение не будет работать).

Таким образом, вы можете попробовать следующее, чтобы получить доступ Далее ваши ключевые элементы и поместить те в your_dictionary:

for node in soup.find_all('td', attrs={'bgcolor':'#FFFFCC'}): 
    your_dictionary[node.string] = node.next_sibling 

Проблема заключается в том, что «next_sibling» на самом деле является «\ п», так что у вас есть сделать следующее, чтобы захватить следующую значения (первое значение, которое вы хотите):

for node in soup.find_all('td', attrs={'bgcolor':'#FFFFCC'}): 
    your_dictionary[node.string] = node.next_sibling.next_sibling.string 

И если вы хотите следующие значения два, вы должны удвоить это:

for node in soup.find_all('td', attrs={'bgcolor':'#FFFFCC'}): 
    your_dictionary[node.string] = [node.next_sibling.next_sibling.string, node.next_sibling.next_sibling.next_sibling.next_sibling.string] 

Отказ от ответственности: эта последняя строка довольно уродливая для меня.

+0

Это своего рода то, что я имел в виду в отношении созданной модели BS иногда становясь на пути больше, чем помогать. Другая слабая ссылка в BS заключается в том, что она * должна * анализировать весь HTML, что делает его хрупким перед лицом странного HTML. Скребки на основе Pyparsing могут выглядеть только для конкретных конструкций в HTML (даже если * они * недействительны HTML - пиражирование не судит) и пропустить остальные. Конечно, использование regex аналогично в подходе, но re заставляет вас указывать все возможные пробелы, верхний/нижний регистр, атрибут HTML - makeHTMLTags и пропуски прокрутки pyparsing, заботятся обо всем этом. – PaulMcG

+0

Согласен. Вот почему я думал, что ваш ответ был лучше, даже если это так, честно говоря, немного над моей головой. Я как бы надеялся, что кто-то укажет на то, что я упустил из виду, что избавит вас от необходимости делать «next_sibling.next_sibling» (это на самом деле трюк, который я узнал, прочитав документы BeautifulSoup). Я думал о некоторых тестах регулярных выражений или пробелов, чтобы убедиться, что мы собираем запрашиваемые материалы, но это не избавляет от уродливой линии.Я также рассматривал какой-то объект генератора, который выглядит более чистым, но я не продолжал его обдумывать. – erewok

+1

Хм, и не только все это 'next_sibling'ing уродливое, оно также очень чувствительно к пробелам - если строки' 'были в одной строке, без промежуточных строк или пробелов, тогда вы бы использовали меньше' next_sibling 'вызывает итерацию через DOM, созданный BS. Если вы написали простой генератор с именем 'next_element', который выполнил бы вызовы' next_sibling', пока не найдет узел элемента (следующий узел ''), вы можете использовать' next_element (node) .string, next_element (next_element (node)) .string' - может быть, немного менее уродливым, но определенно более прочным перед непредсказуемыми пробелами. – PaulMcG

0

Я использовал BeautifulSoup 3, но это, вероятно, будет работать под 4.

# Import System libraries 
import re 

# Import Custom libraries 
from BeautifulSoup import BeautifulSoup 

# This may be different between BeautifulSoup 3 and BeautifulSoup 4 
with open("fide.html") as file_h: 
    # Read the file into the BeautifulSoup class 
    soup = BeautifulSoup(file_h.read()) 

tr_location = lambda x: x.name == u"tr" # Row location 
key_location = lambda x: x.name == u"td" and bool(set([(u"bgcolor", u"#FFFFCC")]) & set(x.attrs)) # Integer key location 
td_location = lambda x: x.name == u"td" and not dict(x.attrs).has_key(u"bgcolor") # Float value location 

str_key_dict = {} 
num_key_dict = {} 
for tr in soup.findAll(tr_location): # Loop through all found rows 
    for key in tr.findAll(key_location): # Loop through all found Integer key tds 
     key_list = [] 
     key_str = key.text.strip() 
     for td in key.findNextSiblings(td_location)[:2]: # Loop through the next 2 neighbouring Float values 
      key_list.append(td.text) 
     key_list = map(float, key_list) # Convert the text values to floats 

     # String based dictionary section 
     str_key_dict[key_str] = key_list 

     # Number based dictionary section 
     num_range = map(int, re.split("\s*-\s*", key_str)) # Extract a value range to perform interpolation 
     if(len(num_range) == 2): 
      num_key_dict.update([(x, key_list) for x in range(num_range[0], num_range[1] + 1)]) 
     else: 
      num_key_dict.update([(num_range[0], key_list)]) 

for x in num_key_dict.items(): 
    print x