Как найти дочерние узлы с помощью BeautifulSoup

#python #html #beautifulsoup

#python #HTML #beautifulsoup

Вопрос:

Я хочу получить все <a> теги, которые являются дочерними элементами <li> :

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

Я знаю, как найти элемент с определенным классом, подобный этому:

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

Но я не знаю, как найти все, <a> которые являются дочерними элементами <li class=test> , но не какие-либо другие.

Как я хочу выбрать:

 <a>link1</a>
  

Ответ №1:

Попробуйте это

 li = soup.find('li', {'class': 'text'})
children = li.findChildren("a" , recursive=False)
for child in children:
    print(child)
  

Комментарии:

1. Или, чтобы просто извлечь выражение, которое описывает то, что мы хотим: soup.find('li', {'class': 'text'}).findChildren() .

2. но как получить только первый тег <a>, а не после подопечных. что-то вроде find(li).find(a).firstChild()

3. Спасибо за «рекурсивный» kwarg 🙂

4. с сайта bs4: «findChildren, findChild: эти методы остались от API Beautiful Soup 2. Они устарели с 2006 года и их вообще не следует использовать: »

Ответ №2:

В документах есть очень маленький раздел, который показывает, как найти / find_all прямых дочерних элементов.

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

В вашем случае, как вы хотите, link1, который является первым прямым дочерним элементом:

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

Если вы хотите, чтобы все прямые дочерние элементы:

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

Ответ №3:

Возможно, вы хотите сделать

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

Комментарии:

1. я думаю, что он тоже найдет <a> link2 </a> , но я этого не хочу

2. Это отвечает на вопрос, как выбрать <a>link1</a> в HTML, приведенном в вопросе, но это приведет к СБОЮ, когда первый <li class="test"> не будет содержать <a> элементов, а есть другие li элементы с test классом, который содержит <a> .

3. Это не отвечает на вопрос, но это было то, что я искал.

Ответ №4:

попробуйте это:

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

другие напоминания:

Метод find получает только первый встречающийся дочерний элемент. Метод find_all получает все элементы-потомки и сохраняется в виде списка.

Комментарии:

1. Спрашивающий не хочет ни одного из двух вариантов выше. Ему нужны все ссылки, которые являются только прямыми дочерними узлами.

Ответ №5:

«Как найти все, a которые являются дочерними элементами <li class=test> , но не какие-либо другие?»

Учитывая приведенный ниже HTML (я добавил другой, <a> чтобы показать разницу между select и select_one ):

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

Решение заключается в использовании дочернего комбинатора ( > ), который размещается между двумя CSS-селекторами:

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

В случае, если вы хотите найти только первый дочерний элемент:

 >>> soup.select_one('li.test > a')
<a>link1</a>
  

Комментарии:

1. Это то, что я искал. Я предоставлял его неправильному методу. Забыл> это CSS-селектор. Спасибо!

2. Как бы вы выбрали только первый <a>, если бы их было несколько в li.test?

Ответ №6:

Еще один метод — создайте функцию фильтра, которая возвращает True для всех желаемых тегов:

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

Затем просто вызовите find_all с аргументом:

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

Ответ №7:

Только что наткнулся на этот ответ и проверил документацию, чтобы увидеть, что soup.findChildren он устарел (BS 4.9). Вместо этого вы можете использовать soup.children , который учитывает только прямых дочерних элементов элемента, а не его потомков.

 li = soup.find('li', {'class': 'text'})
for child in li.children:
    print(child)
  

Документация: https://www.crummy.com/software/BeautifulSoup/bs4/doc/#contents-and-children