Comment trouver des enfants de nœuds à l'aide de BeautifulSoup

115

Je veux obtenir toutes les <a>balises qui sont des enfants de <li>:

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

Je sais comment trouver un élément avec une classe particulière comme celle-ci:

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

Mais je ne sais pas comment trouver tous ceux <a>qui sont enfants de <li class=test>mais pas d'autres.

Comme je veux sélectionner:

<a>link1</a>
tej.tan
la source

Réponses:

124

Essaye ça

li = soup.find('li', {'class': 'text'})
children = li.findChildren("a" , recursive=False)
for child in children:
    print child
cerberos
la source
3
Ou, pour en extraire tout l'expression qui décrit ce que nous voulons: soup.find('li', {'class': 'text'}).findChildren().
Karl Knechtel
3
mais comment obtenir la première balise <a> seulement pas après les wards. quelque chose commefind(li).find(a).firstChild()
tej.tan
Merci pour le kwarg "récursif" :)
Swift
122

Il y a une très petite section dans les DOCs qui montre comment trouver / find_all enfants directs .

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

Dans votre cas, comme vous le souhaitez, link1 qui est le premier enfant direct:

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

Si vous voulez tous les enfants directs:

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

Tu veux peut-être faire

soup.find("li", { "class" : "test" }).find('a')
Bemmu
la source
1
Je pense que ça trouvera <a> link2 </a>aussi mais je ne veux pas ça
tej.tan
1
Cela répond à la question de savoir comment sélectionner <a>link1</a>dans le HTML donné dans la question, mais cela échouera lorsque le premier <li class="test">ne contiendra aucun <a>élément et qu'il y a d'autres liéléments avec la testclasse qui contient <a>.
radzak
11

essaye ça:

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

autres rappels:

La méthode find obtient uniquement le premier élément enfant qui se produit. La méthode find_all récupère tous les éléments descendants et sont stockés dans une liste.

kiiru
la source
2
L'interlocuteur ne veut aucune des deux options ci-dessus. Il veut tous les liens qui ne sont que des enfants directs.
Ahsan Roy
9

"Comment trouver tous ceux aqui sont enfants <li class=test>mais pas d'autres?"

Compte tenu du HTML ci-dessous (j'en ai ajouté un autre <a>pour montrer la différence entre selectet select_one):

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

La solution consiste à utiliser le combinateur enfant ( >) placé entre deux sélecteurs CSS:

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

Au cas où vous souhaiteriez ne trouver que le premier enfant:

>>> soup.select_one('li.test > a')
<a>link1</a>
radzak
la source
C'est celui que je cherchais. Je l'ai fourni à la mauvaise méthode. Forgot> est un sélecteur CSS. Merci!
LFMekz
7

Encore une autre méthode - créez une fonction de filtre qui renvoie Truepour toutes les balises souhaitées:

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

Ensuite, appelez simplement find_allavec l'argument:

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