Cómo encontrar hijos de nodos usando BeautifulSoup

115

Quiero obtener todas las <a>etiquetas que son secundarias de <li>:

<div>
<li class="test">
    <a>link1</a>
    <ul> 
       <li>  
          <a>link2</a> 
       </li>
    </ul>
</li>
</div>

Sé cómo encontrar un elemento con una clase particular como esta:

soup.find("li", { "class" : "test" }) 

Pero no sé cómo encontrar a todos los <a>que son hijos de <li class=test>pero no a los demás.

Como quiero seleccionar:

<a>link1</a>
tej.tan
fuente

Respuestas:

124

Prueba esto

li = soup.find('li', {'class': 'text'})
children = li.findChildren("a" , recursive=False)
for child in children:
    print child
cerberos
fuente
3
O, simplemente extraer la expresión que describe lo que queremos: soup.find('li', {'class': 'text'}).findChildren().
Karl Knechtel
3
pero cómo obtener la primera etiqueta <a> solo no después de las salas. algo comofind(li).find(a).firstChild()
tej.tan
Gracias por el kwarg "recursivo" :)
Swift
122

Hay una sección muy pequeña en los DOC que muestra cómo buscar / encontrar a todos los niños directos .

https://www.crummy.com/software/BeautifulSoup/bs4/doc/#the-recursive-argument

En su caso, como desea link1, que es el primer hijo directo:

# for only first direct child
soup.find("li", { "class" : "test" }).find("a", recursive=False)

Si quieres todos los hijos directos:

# for all direct children
soup.find("li", { "class" : "test" }).findAll("a", recursive=False)
strider
fuente
12

Tal vez quieras hacer

soup.find("li", { "class" : "test" }).find('a')
Bemmu
fuente
1
Creo que también encontrará <a> link2 </a>, pero no quiero eso
tej.tan
1
Esto responde a la pregunta de cómo seleccionar <a>link1</a>en el HTML dado en la pregunta, pero esto FALLARÁ cuando el primero <li class="test">no contenga <a>elementos y haya otros lielementos con la testclase que contiene <a>.
radzak
11

prueba esto:

li = soup.find("li", { "class" : "test" })
children = li.find_all("a") # returns a list of all <a> children of li

otros recordatorios:

El método de búsqueda solo obtiene el primer elemento hijo que aparece. El método find_all obtiene todos los elementos descendientes y se almacenan en una lista.

kiiru
fuente
2
El interrogador no quiere ninguna de las dos opciones anteriores. Quiere todos los enlaces que son solo hijos directos.
Ahsan Roy
9

"Cómo encontrar todos los aque son hijos de<li class=test> pero no los demás?"

Dado el HTML a continuación (agregué otro <a>para mostrar la diferencia entre selecty select_one):

<div>
  <li class="test">
    <a>link1</a>
    <ul>
      <li>
        <a>link2</a>
      </li>
    </ul>
    <a>link3</a>
  </li>
</div>

La solución es usar un combinador secundario ( >) que se coloca entre dos selectores CSS:

>>> soup.select('li.test > a')
[<a>link1</a>, <a>link3</a>]

En caso de que quiera encontrar solo al primer hijo:

>>> soup.select_one('li.test > a')
<a>link1</a>
radzak
fuente
Este es el que estaba buscando. Lo estaba suministrando al método equivocado. Forgot> es un selector de CSS. ¡Gracias!
LFMekz
7

Otro método más: cree una función de filtro que devuelva Truetodas las etiquetas deseadas:

def my_filter(tag):
    return (tag.name == 'a' and
        tag.parent.name == 'li' and
        'test' in tag.parent['class'])

Luego solo llama find_allcon el argumento:

for a in soup(my_filter): # or soup.find_all(my_filter)
    print a
Dedek Mraz
fuente