2015-05-30 2 views
1

При использовании BeautifulSoup4 я могу запустить этот код, чтобы получить один «крик» без проблем. Когда я использую цикл for, я получаю ошибку AttributeError: 'NavigableString' object has no attribute 'children'BeautifulSoup: AttributeError: объект 'NavigableString' не имеет атрибута 'children'

class Shout: 
    def __init__(self, user, msg, date): 
     self.user = user 
     self.msg = msg 
     self.date = date 

def getShouts(): 
    #s is a requests Session() 
    new_shouts = s.get(shouts_url).text 
    #set shouts page as parsable object 
    soup = BeautifulSoup(new_shouts) 
    shouts = [] 
    shout_heads = soup.find_all("h2", {'class': 'A'}) 
    shout_feet = soup.find_all("h2", {'class': 'B'}) 
    for i in range(len(shout_heads)): 
     shout = Shout('', '', '') 
     shout.user = list(list(list(shout_heads[i].children)[0].children)[1].children)[1].get_text() 
     foot = shout_feet[i].get_text().split('-') 
     shout.msg = foot[1] 
     foot[2] = foot[2].split() 
     shout.date = foot[2][0] + " " + foot[2][1] 
     shouts.append(shout) 
    return shouts 

Что бы причиной этой ошибки происходит только во время цикла?

ответ

1

children содержит не только теги в элементе, но и любые текст (с изображениями NavigableString объектов). Даже пробелы могут привести к тексту перед первым элементом:

<h2> 
    <a href="...">Some text</a> 
</h2> 

будет иметь текстовый узел в качестве первого ребенка. Вам нужно будет отфильтровать эти текстовые узлы или использовать element.find_all(True, recursive=False), чтобы перечислять только теги прямого дочернего элемента. element.find(True) находит первый дочерний тег, или None, если таких тегов нет.

Или, возможно, вы можете искать более конкретные теги, чем только первый ребенок, а затем второй ребенок, а затем второй chid; если у вас есть конкретные теги, то просто используйте их имя:

shout_heads[i].a.i.span.string 

например.

Обратите внимание, что .children дает вам итератор; если вам нужен список, * не используйте list() на .children. Вместо этого используйте атрибут .contents, который является объектом списка.

Последнее, но не менее, не следует использовать петлю над range(), когда вы можете перебираем список непосредственно:

for shout_head in shout_heads: 
    shout = Shout('', '', '') 
    shout.user = shout_head.find(True)[0] # etc. 

Если вам нужно объединить два списка, вы можете использовать zip():

for shout_head, shout_foot in zip(shout_heads, shout_feet): 

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

+0

Прежде всего, спасибо за тонну за то, что помогли мне избавиться от всех этих вызовов 'list()'. Я посмотрю, что я могу сделать, чтобы отфильтровать текстовые узлы, но я был убежден, что каждый «Shout» будет отформатирован одинаково (без дополнительных узлов). – kaloncpu57

+0

Теперь я вижу, что мне нужно будет еще немного фильтровать, потому что 'shout_feet [i] .get_text()' возвращается больше, чем хотелось бы. Что касается циклов над 'range()', я хотел создать 'Shout()' в одном цикле. A 'Shout()' берет как от 'shout_heads', так и от' shout_feet', и я не хотел напрямую перебирать один список. – kaloncpu57

+1

@ kaloncpu57: извините, я пропустил это. Вы можете использовать 'zip()' в этом случае. –

Смежные вопросы