Как мне учитывать предка элемента при разборе с BeautifulSoup? - PullRequest
1 голос
/ 05 апреля 2019

Я использую Python 3.7, Django и BeautifulSoup. В настоящее время я ищу элементы "span" в моем документе, которые содержат текст "Review". Мне так нравится

html = urllib2.urlopen(req, timeout=settings.SOCKET_TIMEOUT_IN_SECONDS).read()

my_soup = BeautifulSoup(html, features="html.parser")

rev_elts = my_soup.findAll("span", text=re.compile("Review"))
for rev_elt in rev_elts:
    ... processing

но я хотел бы добавить складку туда, где я не хочу рассматривать эти элементы, если у них есть предок DIV с классом "child". Так, например, я не хочу рассматривать что-то вроде этого

    <div class="child">
        <p>
            <span class="s">Reviews</span>
        ...
    </p>
</div>

Как мне настроить поиск, чтобы учесть это?

Ответы [ 2 ]

1 голос
/ 06 апреля 2019

Селектор CSS - это то, что нужно, так как @facelessuser ответил. Но на тот случай, если вам интересно, это можно сделать без использования селектора CSS.

Вы можете перебрать всех родителей элемента с помощью .parents . Вы можете определить пользовательскую функцию фильтра , которая проверяет, имеет ли какой-либо из родителей класс "child" и возвращает True в противном случае (в дополнение ко всем другие ваши условия).

from bs4 import BeautifulSoup, Tag
html="""
<div class="child">
<p><span id="1">Review</span></p>
</div>
<div>
<p><span id="2">Review</span></p>
</div>
"""
soup=BeautifulSoup(html,'html.parser')
def my_func(item):
    if isinstance(item,Tag) and item.name=='span' and 'Review' in item.text:
        for parent in item.parents:
            if parent.has_attr('class'):
                if 'child' in parent.get('class'):
                    return False
        return True
my_spans=soup.find_all(my_func)
print(my_spans)

Выходы:

[<span id="2">Review</span>]
1 голос
/ 05 апреля 2019

Если вы используете BeautifulSoup 4.7+, в нем улучшена поддержка CSS-селекторов.Он обрабатывает множество селекторов до уровня CSS 4 и несколько пользовательских, таких как :contains().В дополнение ко всему этому, он обрабатывает сложные селекторы в псевдоклассах, таких как :not(), который должен обрабатывать уровень 4, но недавно они выдвинули эту поддержку в поддержку селектора уровня 5 CSS.

в этом примере мы будем использовать пользовательский селектор :contains для поиска диапазонов, которые содержат текст Review.Кроме того, мы скажем, что не хотим, чтобы оно совпадало с div.class span.

from bs4 import BeautifulSoup

html = """
<div>
    <p><span>Review: Let's find this</span></p>
</div>
<div class="child">
    <p><span>Review: Do not want this</span></p>
</div>
"""

soup = BeautifulSoup(html, features="html.parser")

spans = soup.select('span:contains(Review):not(div.child span)')
print(spans)

Выход

[<span>Review: Let's find this</span>]    

В зависимости от вашего случая, возможно :contains isnнедостаточно крепкийВ этом случае вы все еще можете сделать что-то подобное.Soup Sieve - это базовая библиотека, включенная в Beautiful Soup 4.7+, и вы можете импортировать ее напрямую, чтобы отфильтровать возвраты регулярного выражения:

from bs4 import BeautifulSoup
import soupsieve as sv
import re

html = """
<div>
    <p><span>Review: Let's find this</span></p>
</div>
<div class="child">
    <p><span>Review: Do not want this</span></p>
</div>
"""

soup = BeautifulSoup(html, features="html.parser")

spans = soup.find_all("span", text=re.compile("Review"))
spans = sv.filter(':not(div.child span)', spans)

print(spans)

Вывод

[<span>Review: Let's find this</span>]   
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...