Есть ли более питонический способ объединить две строки заголовка HTML с colspans?

Я использую BeautifulSoup в Python для синтаксического анализа HTML. Одна из проблем, с которой я имею дело, заключается в том, что у меня бывают ситуации, когда colspans различаются в строках заголовков. (Строки заголовков - это строки, которые необходимо объединить, чтобы получить заголовки столбцов на моем жаргоне). То есть один столбец может охватывать несколько столбцов выше или ниже него, и слова необходимо добавлять или добавлять в начале в зависимости от диапазона. Ниже приведен порядок действий для этого. Я использую BeautifulSoup для извлечения столбцов и содержимого каждой ячейки в каждой строке. longHeader - это содержимое строки заголовка с наибольшим количеством элементов, spanLong - это список с colspans каждого элемента в строке. Это работает, но выглядит не очень Pythonic.

Алос - это не сработает, если разница <0, я могу исправить это тем же подходом, который я использовал, чтобы заставить это работать. Но прежде чем я это сделаю, мне интересно, сможет ли кто-нибудь быстро взглянуть на это и предложить более питонический подход. Я долгое время являюсь программистом SAS и изо всех сил стараюсь сломать шаблон. Я буду писать код так, как будто я пишу макрос SAS.

longHeader=['','','bananas','','','','','','','','','','trains','','planes','','','','']
shortHeader=['','','bunches','','cars','','trucks','','freight','','cargo','','all other','','']
spanShort=[1,1,3,1,3,1,3,1,3,1,3,1,3,1,3]
spanLong=[1,1,3,1,1,1,1,1,1,1,1,1,3,1,3,1,3,1,3]
combinedHeader=[]
sumSpanLong=0
sumSpanShort=0
spanDiff=0
longHeaderCount=0

for each in range(len(shortHeader)):
    sumSpanLong=sumSpanLong+spanLong[longHeaderCount]
    sumSpanShort=sumSpanShort+spanShort[each]
    spanDiff=sumSpanShort-sumSpanLong
    if spanDiff==0:
        combinedHeader.append([longHeader[longHeaderCount]+' '+shortHeader[each]])
        longHeaderCount=longHeaderCount+1
        continue
    for i in range(0,spanDiff):
            combinedHeader.append([longHeader[longHeaderCount]+' '+shortHeader[each]])
            longHeaderCount=longHeaderCount+1
            sumSpanLong=sumSpanLong+spanLong[longHeaderCount]
            spanDiff=sumSpanShort-sumSpanLong
            if spanDiff==0:
                combinedHeader.append([longHeader[longHeaderCount]+' '+shortHeader[each]])
                longHeaderCount=longHeaderCount+1
                break

print combinedHeader
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
656
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Возможно, посмотрите на функцию zip, чтобы узнать о некоторых частях проблемы:

>>> execfile('so_ques.py')
[[' '], [' '], ['bananas bunches'], [' '], [' cars'], [' cars'], [' cars'], [' '], [' trucks'], [' trucks'], [' trucks'], [' '], ['trains freight'], [' '], ['planes cargo'], [' '], [' all other'], [' '], [' ']]

>>> zip(long_header, short_header)
[('', ''), ('', ''), ('bananas', 'bunches'), ('', ''), ('', 'cars'), ('', ''), ('', 'trucks'), ('', ''), ('', 'freight'), ('', ''), ('', 'cargo'), ('', ''), ('trains', 'all other'), ('', ''), ('planes', '')]
>>> 

enumerate может помочь избежать сложной индексации со счетчиками:

>>> diff_list = []
>>> for place, header in enumerate(short_header):
    diff_list.append(abs(span_short[place] - span_long[place]))

>>> for place, num in enumerate(diff_list):
    if num:
        new_shortlist.extend(short_header[place] for item in range(num+1))
    else:
        new_shortlist.append(short_header[place])


>>> new_shortlist
['', '', 'bunches', '', 'cars', 'cars', 'cars', '', 'trucks', 'trucks', 'trucks', '',... 
>>> z = zip(new_shortlist, long_header)
>>> z
[('', ''), ('', ''), ('bunches', 'bananas'), ('', ''), ('cars', ''), ('cars', ''), ('cars', '')...

Также ясность может добавить более питоническое именование:

    for each in range(len(short_header)):
        sum_span_long += span_long[long_header_count]
        sum_span_short += span_short[each]
        span_diff = sum_span_short - sum_span_long
        if not span_diff:
            combined_header.append...

Какие «соглашения» вы изменили при репосте исходного кода?

S.Lott 10.11.2008 14:15

Используемые имена PEP 8, измените a = a + 1 на a + = 1, просто приведите его в соответствие с рекомендуемым стилем

unmounted 10.11.2008 22:39

@bvmou: Вот в чем суть - это длинный пост об этом небольшом изменении - изменении настолько маленьком, что я не мог его обнаружить и должен был спросить.

S.Lott 11.11.2008 00:06

Вот модифицированная версия вашего алгоритма. застегивать используется для перебора длин и заголовков короткая, а объект класса используется для подсчета и перебора элементов длинная, а также объединения заголовков. пока больше подходит для внутреннего цикла. (простите за слишком короткие имена).

class collector(object):
    def __init__(self, header):
        self.longHeader = header
        self.combinedHeader = []
        self.longHeaderCount = 0
    def combine(self, shortValue):
        self.combinedHeader.append(
            [self.longHeader[self.longHeaderCount]+' '+shortValue] )
        self.longHeaderCount += 1
        return self.longHeaderCount

def main():
    longHeader = [ 
       '','','bananas','','','','','','','','','','trains','','planes','','','','']
    shortHeader = [
    '','','bunches','','cars','','trucks','','freight','','cargo','','all other','','']
    spanShort=[1,1,3,1,3,1,3,1,3,1,3,1,3,1,3]
    spanLong=[1,1,3,1,1,1,1,1,1,1,1,1,3,1,3,1,3,1,3]
    sumSpanLong=0
    sumSpanShort=0

    combiner = collector(longHeader)
    for sLen,sHead in zip(spanShort,shortHeader):
        sumSpanLong += spanLong[combiner.longHeaderCount]
        sumSpanShort += sLen
        while sumSpanShort - sumSpanLong > 0:
            combiner.combine(sHead)
            sumSpanLong += spanLong[combiner.longHeaderCount]
        combiner.combine(sHead)

    return combiner.combinedHeader
Ответ принят как подходящий

В этом примере на самом деле много чего происходит.

  1. Вы «чрезмерно переработали» объекты Beautiful Soup Tag, чтобы составить списки. Оставьте их как теги.

  2. Все эти виды алгоритмов слияния сложны. Это помогает симметрично относиться к двум объединяемым объектам.

Вот версия, которая должна работать напрямую с объектами Beautiful Soup Tag. Кроме того, эта версия ничего не предполагает о длине двух строк.

def merge3( row1, row2 ):
    i1= 0
    i2= 0
    result= []
    while i1 != len(row1) or i2 != len(row2):
        if i1 == len(row1):
            result.append( ' '.join(row1[i1].contents) )
            i2 += 1
        elif i2 == len(row2):
            result.append( ' '.join(row2[i2].contents) )
            i1 += 1
        else:
            if row1[i1]['colspan'] < row2[i2]['colspan']:
                # Fill extra cols from row1
                c1= row1[i1]['colspan']
                while c1 != row2[i2]['colspan']:
                    result.append( ' '.join(row2[i2].contents) )
                    c1 += 1
            elif row1[i1]['colspan'] > row2[i2]['colspan']:
                # Fill extra cols from row2
                c2= row2[i2]['colspan']
                while row1[i1]['colspan'] != c2:
                    result.append( ' '.join(row1[i1].contents) )
                    c2 += 1
            else:
                assert row1[i1]['colspan'] == row2[i2]['colspan']
                pass
            txt1= ' '.join(row1[i1].contents)
            txt2= ' '.join(row2[i2].contents)
            result.append( txt1 + " " + txt2 )
            i1 += 1
            i2 += 1
    return result

Думаю, я собираюсь ответить на свой вопрос, но мне действительно очень помогли. Спасибо за помощь. Я заставил ответ S.LOTT работать после нескольких небольших исправлений. (Они могут быть настолько маленькими, что их не будет видно (внутренняя шутка)). Итак, теперь вопрос в том, почему это больше Pythonic? Я думаю, что вижу, что он менее плотный / работает с необработанными входными данными вместо производных / я не могу судить, легче ли его читать ---> хотя это легко читать

Ответ S.LOTT исправлен.

row1=headerCells[0]
row2=headerCells[1]

i1= 0
i2= 0
result= []
while i1 != len(row1) or i2 != len(row2):
    if i1 == len(row1):
        result.append( ' '.join(row1[i1]) )
        i2 += 1
    elif i2 == len(row2):
        result.append( ' '.join(row2[i2]) )
        i1 += 1
    else:
        if int(row1[i1].get("colspan","1")) < int(row2[i2].get("colspan","1")):
            c1= int(row1[i1].get("colspan","1"))
            while c1 != int(row2[i2].get("colspan","1")): 
                txt1= ' '.join(row1[i1])  # needed to add when working adjust opposing case
                txt2= ' '.join(row2[i2])     # needed to add  when working adjust opposing case
                result.append( txt1 + " " + txt2 )  # needed to add when working adjust opposing case
                print 'stayed in middle', 'i1=',i1,'i2=',i2, ' c1=',c1
                c1 += 1
                i1 += 1    # Is this the problem it
           
        elif int(row1[i1].get("colspan","1"))> int(row2[i2].get("colspan","1")):
                # Fill extra cols from row2  Make same adjustment as above
            c2= int(row2[i2].get("colspan","1"))
            while int(row1[i1].get("colspan","1")) != c2:
                result.append( ' '.join(row1[i1]) )
                c2 += 1
                i2 += 1
        else:
            assert int(row1[i1].get("colspan","1")) == int(row2[i2].get("colspan","1"))
            pass
       
    
        txt1= ' '.join(row1[i1])
        txt2= ' '.join(row2[i2])
        result.append( txt1 + " " + txt2 )
        print 'went to bottom', 'i1=',i1,'i2=',i2
        i1 += 1
        i2 += 1
print result

1. Не стесняйтесь использовать определения функций, чтобы облегчить чтение. 2. Примите ответ.

S.Lott 11.11.2008 15:17

Я пока не собираюсь принимать ответ, так как он не особенно хорош, хотя я многому научился из предоставленных ответов. Отличный ответ сработает в общем случае, и он сработает с первого раза из коробки. Мне все еще нужны строки> 2. Я хочу попробовать два других ответа

PyNEwbie 11.11.2008 23:28

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

Чтобы сэкономить время на создание тестовых примеров, я собираюсь вставить полный код, с которым я работал, в IDLE, и последую за ним с помощью образца файла HTML. Помимо принятия решения о классе / функции (и мне нужно подумать о том, как я использую этот код в своей программе), я был бы рад увидеть любые улучшения, которые сделают код более Pythonic.

from BeautifulSoup import BeautifulSoup

original=file(r"C:\testheaders.htm").read()

soupOriginal=BeautifulSoup(original)
all_Rows=soupOriginal.findAll('tr')


header_Rows=[]
for each in range(len(all_Rows)):
    header_Rows.append(all_Rows[each])


header_Cells=[]
for each in header_Rows:
    header_Cells.append(each.findAll('td'))

temp_Header_Row=[]
header=[]
for row in range(len(header_Cells)):
    for column in range(len(header_Cells[row])):
        x=int(header_Cells[row][column].get("colspan","1"))
        if x==1:
            temp_Header_Row.append( ' '.join(header_Cells[row][column]) )

        else:
            for item in range(x):

                temp_Header_Row.append( ''.join(header_Cells[row][column]) )

    header.append(temp_Header_Row)
temp_Header_Row=[]
combined_Header=zip(*header)

for each in combined_Header:
    print each

Хорошо, содержимое тестового файла ниже. Извините, я попытался прикрепить их, но не смог:

  <TABLE style = "font-size: 10pt" cellspacing = "0" border = "0" cellpadding = "0" width = "100%">
  <TR valign = "bottom">
  <TD width = "40%">&nbsp;</TD>
  <TD width = "5%">&nbsp;</TD>
  <TD width = "3%">&nbsp;</TD>
  <TD width = "3%">&nbsp;</TD>
  <TD width = "1%">&nbsp;</TD>

  <TD width = "5%">&nbsp;</TD>
  <TD width = "3%">&nbsp;</TD>
  <TD width = "3%">&nbsp;</TD>
  <TD width = "1%">&nbsp;</TD>

  <TD width = "5%">&nbsp;</TD>
  <TD width = "3%">&nbsp;</TD>
  <TD width = "1%">&nbsp;</TD>
  <TD width = "1%">&nbsp;</TD>

  <TD width = "5%">&nbsp;</TD>
  <TD width = "3%">&nbsp;</TD>
  <TD width = "1%">&nbsp;</TD>
  <TD width = "1%">&nbsp;</TD>

  <TD width = "5%">&nbsp;</TD>
  <TD width = "3%">&nbsp;</TD>
  <TD width = "3%">&nbsp;</TD>
  <TD width = "1%">&nbsp;</TD>
  </TR>
  <TR style = "font-size: 10pt" valign = "bottom">
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD nowrap align = "right" colspan = "2">FOODS WE LIKE</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD nowrap align = "right" colspan = "2">&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD nowrap align = "right" colspan = "2">&nbsp;</TD>
  <TD>&nbsp;</TD>
  </TR>
  <TR style = "font-size: 10pt" valign = "bottom">
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD nowrap align = "CENTER" colspan = "6">SILLY STUFF</TD>

  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD nowrap align = "right" colspan = "2">OTHER THAN</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD nowrap align = "CENTER" colspan = "6">FAVORITE PEOPLE</TD>
  <TD>&nbsp;</TD>
  </TR>
  <TR style = "font-size: 10pt" valign = "bottom">
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD nowrap align = "right" colspan = "2">MONTY PYTHON</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD nowrap align = "right" colspan = "2">CHERRYPY</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD nowrap align = "right" colspan = "2">APPLE PIE</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD nowrap align = "right" colspan = "2">MOTHERS</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD nowrap align = "right" colspan = "2">FATHERS</TD>
  <TD>&nbsp;</TD>
  </TR>
  <TR style = "font-size: 10pt" valign = "bottom">
  <TD nowrap align = "left">Name</TD>
  <TD>&nbsp;</TD>
  <TD nowrap align = "right" colspan = "2">SHOWS</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD nowrap align = "right" colspan = "2">PROGRAMS</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD nowrap align = "right" colspan = "2">BANANAS</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD nowrap align = "right" colspan = "2">PERFUME</TD>
  <TD>&nbsp;</TD>
  <TD>&nbsp;</TD>
  <TD nowrap align = "right" colspan = "2">TOOLS</TD>
  <TD>&nbsp;</TD>
  </TR>
  </TABLE>

Другие вопросы по теме