Как найти потомков узлов с помощью BeautifulSoup

119

Я хочу получить все <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>
tej.tan
источник

Ответы:

126

Попробуй это

li = soup.find('li', {'class': 'text'})
children = li.findChildren("a" , recursive=False)
for child in children:
    print child
Cerberos
источник
3
Или просто извлечь выражение , которое описывает то , что мы хотим: soup.find('li', {'class': 'text'}).findChildren().
Karl Knechtel
3
но как получить тег frist <a> только не после вардов. что-то вродеfind(li).find(a).firstChild()
tej.tan 09
Спасибо за "рекурсивный" kwarg :)
Swift
124

В DOC есть очень маленький раздел, в котором показано, как найти / 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)
Strider
источник
13

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

soup.find("li", { "class" : "test" }).find('a')
Бемму
источник
1
я думаю, что он тоже найдет, <a> link2 </a>но я не хочу этого
tej.tan 09
1
Это отвечает на вопрос, как выбрать <a>link1</a>в HTML, заданном в вопросе, но это будет НЕОБХОДИМО, если первый не <li class="test">будет содержать <a>элементов и есть другие liэлементы с testклассом, который содержит <a>.
radzak
11

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

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

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

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

Кииру
источник
2
Запрашивающий не хочет ни одного из двух вышеперечисленных вариантов. Ему нужны все ссылки, которые являются только прямым потомком.
Ahsan Roy
9

«Как найти всех 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>
Радзак
источник
Это то, что я искал. Я использовал неправильный метод. Forgot> - это селектор CSS. Спасибо!
Л.Ф.Мекз
7

Еще один способ - создать функцию фильтра, возвращающую 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
Дедек Мраз
источник