漂亮汤获取第一个子元素

22

如何获取第一个子元素?

 <div class="cities"> 
       <div id="3232"> London </div>
       <div id="131"> York </div>
  </div>

我怎样才能得到伦敦?

for div in nsoup.find_all(class_='cities'):
    print (div.children.contents)

AttributeError: 'listiterator'对象没有属性'contents'


1
children 是一个子序列,每个子序列都有 contents。这个序列本身没有 contents。就像 [1, 2, 3] 不是一个整数一样。同时,和任何序列一样,要获取第一个值,只需使用 next(iter(div.children)) - abarnert
3个回答

15
使用现代版本的bs4(至少bs4 4.7.1+),您可以访问:first-child css伪类选择器。使用soup.select_one如果只想返回第一项匹配,例如soup.select_one('.cities div:first-child').text。在使用.text访问器之前测试是否为None被认为是最佳实践。
from bs4 import BeautifulSoup as bs

html = '''
<div class="cities"> 
       <div id="3232"> London </div>
       <div id="131"> York </div>
  </div>
  '''
soup = bs(html, 'lxml') #or 'html.parser'
first_children = [i.text for i in soup.select('.cities div:first-child')]
print(first_children)

你是怎么知道 CSS 选择器的正确语法的?我无法轻易地找到如何制作一个选择具有特定类的父元素的第一个子元素的 CSS 选择器。我的初步猜测是它将是 element.class:first-child,因为 element.class 用于按类选择元素。而且 w3 SchoolsMDN Web Docs 都没有提供如何做到这一点的建议。 - Übermensch
1
https://developer.mozilla.org/zh-CN/docs/Learn/CSS/Building_blocks/Selectors/Combinators - QHarr

14
children方法返回一个迭代器。
for div in nsoup.find_all(class_='cities'):
    for childdiv in div.find_all('div'):
        print (childdiv.string) #london, york

由于类似于'\n'这样的非标签存在于.children中,因此引发了AttributeError异常。只需使用正确的子选择器来查找特定的div即可。

(更多编辑)无法重现您的异常-以下是我所做的:

In [137]: print foo.prettify()
<div class="cities">
 <div id="3232">
  London
 </div>
 <div id="131">
  York
 </div>
</div>

In [138]: for div in foo.find_all(class_ = 'cities'):
   .....:     for childdiv in div.find_all('div'):
   .....:         print childdiv.string
   .....: 
 London 
 York 

In [139]: for div in foo.find_all(class_ = 'cities'):
   .....:     for childdiv in div.find_all('div'):
   .....:         print childdiv.string, childdiv['id']
   .....: 
 London  3232
 York  131

AttributeError: 'NavigableString' object has no attribute 'contents',当我使用字符串而不是内容时,会出现RuntimeError: maximum recursion depth exceeded while calling a Python object - Emmet B
它将打印出 None、None,然后是 RuntimeError: 调用 Python 对象时超过了最大递归深度。 - Emmet B
2
通过 div['id'] 或类似于 div.get('id', None) 的方式进行检索,就像从 dict 中检索一样。 - thkang

8

当前被接受的答案获取了所有城市,但问题只需要第一个。

如果你只需要第一个子元素,可以利用.children返回迭代器而不是列表的特性。记住迭代器会即时生成列表项,因为我们只需要迭代器的第一个元素,所以我们不需要生成所有其他城市元素(从而节省时间)。

for div in nsoup.find_all(class_='cities'):
    first_child = next(div.children, None)
    if first_child is not None:
        print(first_child.string.strip())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接