BeautifulSoup4 найти все не вложенные совпадения - PullRequest
0 голосов
/ 18 января 2019

У меня проблемы с настройкой простого поиска всех внешних элементов, соответствующих моему запросу, в html-документе.Я спрашиваю здесь с надеждой, что есть простая функция bs4, которая делает это, но это не так.

Рассмотрим следующий html-пример, где я хочу, чтобы все внешние <div> имели класс "wanted" (я ожидаю получить список 2 ):

import bs4

text = """
<div>
    <div class="inner">
        <div class="wanted">
            I want this.
            <div class="wanted">
                I don't want that!
            </div>
        </div>
    </div>
    <div class="inner">
        <div class="wanted">
            I want this too.
        </div>
    </div>
</div>"""

soup = bs4.BeautifulSoup(text, 'lxml')

# 1. Trying all at once
fetched = soup.findAll('div', class_='wanted')
print(len(fetched))  # 3

fetched = soup.findAll('div', class_='wanted', recursive=False)
print(len(fetched))  # 0

fetched = soup.findChildren('div', class_='wanted')
print(len(fetched))  # 3

fetched = soup.findChildren('div', class_='wanted', recursive=False)
print(len(fetched))  # 0


# 2. Trying one after the other
fetched = []
fetched0 = soup.find('div', class_='wanted')

while fetched0:
    fetched.append(fetched0)
    descendants = list(fetched0.descendants)
    fetched0 = descendants[-1].findNext('div', class_='wanted')

print(len(fetched))  # 2  Hurra!

# 3. Destructive method: if you don't care about the parents of this element
fetched = []
fetched0 = soup.find('div', class_='wanted')
while fetched0:
    fetched.append(fetched0.extract())
    fetched0 = soup.find('div', class_='wanted')
print(len(fetched))

Так что ничто в части # 1. не дает ожидаемого результата.Поэтому в чем разница между findAll и findChildren ??И findNextSibling не имеет значения, учитывая вложенность здесь.

Теперь часть # 2. работает, но зачем писать столько кода?Разве нет более элегантного решения?Что касается части # 3., нужно быть осторожным с предполагаемыми последствиями.

Каковы ваши предложения об этом поиске?Я действительно нашел кратчайший путь?Могу ли я использовать магию выбора CSS?

Ответы [ 2 ]

0 голосов
/ 13 марта 2019

Я, наконец, сделал следующее, что дает мне преимущество в том, что он не разрушительный. Кроме того, у меня нет времени на его тестирование, но я просто надеялся, что это позволит избежать прохождения каждого вложенного элемента, как в ответе @ Bitto-Bennichan, но на самом деле это неуверенно. В любом случае он делает то, что хотел:

all_fetched = []
fetched = soup.find('div', class_='wanted')

while fetched is not None:
    all_fetched.append(fetched)
    try:
        last = list(fetched.descendants)[-1]
    except IndexError:
        break
    fetched = last.findNext('div', class_='wanted')
0 голосов
/ 18 января 2019

Вы можете передать функцию в качестве аргумента find_all, в дополнение к другим аргументам. А внутри вы можете проверить с помощью find_parents (), чтобы убедиться, что у него нет div верхнего уровня с тем же классом. Используйте find_parents(), так как он будет проверять всех родителей, а не только его непосредственных родителей, так что вы получите только самый внешний «требуемый» div.

def top_most_wanted(tag):
    children_same_class=tag.find_parents("div", class_="wanted")
    if len(children_same_class) >0:
        return False
    return True
soup=BeautifulSoup(text,'html.parser')
print(soup.find_all(top_most_wanted,'div',class_="wanted"))
...