#python #html #beautifulsoup
#python #HTML #beautifulsoup
Вопрос:
Я очищаю несколько страниц, которые имеют одинаковый формат, но он немного меняется здесь и там, и нет классов, которые можно использовать для поиска того, что мне нужно.
Формат выглядит следующим образом:
<div id="mainContent">
<p>Some Text I don't want</p>
<p>Some Text I don't want</p>
<p>Some Text I don't want</p>
<span> More text I don't want</span>
<ul>...unordered-list items..</ul>
<p>Text I WANT</p>
<ol>...ordered-list items..</ol>
<p>Text I WANT</p>
<ol>...ordered-list items..</ol>
</div>
Количество упорядоченных / неупорядоченных списков и других тегов меняется в зависимости от страницы, но что остается неизменным, так это то, что я всегда хочу текст из <p>
тега, который является предыдущим <ol>
аналогом тега.
Что я пытаюсь (и не работает), так это:
main = soup.find("div", {"id":"mainContent"})
for d in main.children:
if d.name == 'p' and d.nextSibling.name == 'ol':
print(d.text)
else:
print("fail")
Вывод этого выполняется fail
для каждой итерации. Пытаясь выяснить, почему это не работает, я попытался:
for d in main.children:
if d.name == 'p':
print(d.nextSibling.name)
else:
print("fail")
Результат этого выглядит примерно так:
fail
None
fail
None
fail
None
fail
fail
fail
fail
fail
None
fail
и т.д…
Почему это работает не так, как я думаю? Как я могу получить текст из <p>
элемента, только если следующий тег есть <ol>
?
Ответ №1:
Вам нужны только p
теги, которые находятся перед ol
тегом. ol
Сначала найдите теги, а затем найдите предыдущие объекты тегов, которые в данном случае являются p
тегом. Теперь ваш код не работает, потому что между Tag
элементами, которые являются объектами типа NavigableString, есть новая строка. И d.nextSibling
также выдает вам эти новые строки. Поэтому вам нужно проверить тип объекта здесь.
from bs4 import Tag
# create soup
# find the ols
ols = soup.find_all('ol')
for ol in ols:
prev = ol.previous_sibling
while(not isinstance(prev, Tag)):
prev = prev.previous_sibling
print(prev.text)
Это даст вам нужный текст.
Text I WANT
Text I WANT
Комментарии:
1. Ах, ха! Спасибо. Я только что узнал, что могу точно найти его, используя
if d.nextSibling.nextSibling.name == 'ol':
, но я не мог понять, почему мне нужно было использовать двух братьев и сестер, чтобы получить следующего брата.2. Да, это тоже сработает в этом случае. Но что, если обхода только двух братьев и сестер недостаточно? Потому
html
что это может быть непредсказуемо. Я думаю, что проверка типов объектов здесь является хорошей идеей.3. Я определенно согласен. Спасибо
Ответ №2:
Вы можете использовать селектор css, т.е. ul ~ p
Найти все теги p, которым предшествует ul:
html = """<div id="mainContent">
<p>Some Text I don't want</p>
<p>Some Text I don't want</p>
<p>Some Text I don't want</p>
<span> More text I don't want</span>
<ul>...unordered-list items..</ul>
<p>Text I WANT</p>
<ol>...ordered-list items..</ol>
<p>Text I WANT</p>
<ol>...ordered-list items..</ol>
</div>"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html)
print([p.text for p in soup.select("#mainContent ul ~ p")])
Что даст вам:
['Text I WANT', 'Text I WANT']
Или найдите ol, а затем найдите предыдущий родственный p:
print([ol.find_previous_sibling("p").text for ol in soup.select("#mainContent ol")])
Что также даст вам:
['Text I WANT', 'Text I WANT']
Комментарии:
1.
print([p.text for p in soup.select("#mainContent ul ~ p")])
работает, 1 для одной строки. Второй вариант, который вы далиprint([ol.find_previous_sibling("p").text for ol in soup.select("#mainContent ol")])
, даетTypeError: 'NavigableString' object is not callable
2. @DjH, вы уверены, что использовали
find_previous_sibling("p")
, эта ошибка больше похожа на вашуprevious_sibling("p")
.3. О, черт, ваше право. Не копировал и не вставлял его, просто сделал, и это работает >.>
4. Один из них — это атрибут, который получает непосредственный предыдущий аналог, другой — это метод, который ищет то, что вы передаете,
ol.find_previous_sibling().text
также будет работать в этом случае, но использование p гарантирует, что если что-то изменится, вы все равно получите предыдущий аналог p.