Подтвердить что ты не робот

Python BeautifulSoup извлекает текст между элементами

Я пытаюсь извлечь "ЭТО МОЙ ТЕКСТ" из следующего HTML:

<html>
<body>
<table>
   <td class="MYCLASS">
      <!-- a comment -->
      <a hef="xy">Text</a>
      <p>something</p>
      THIS IS MY TEXT
      <p>something else</p>
      </br>
   </td>
</table>
</body>
</html>

Я пробовал это следующим образом:

soup = BeautifulSoup(html)

for hit in soup.findAll(attrs={'class' : 'MYCLASS'}):
    print hit.text

Но я получаю весь текст между всеми вложенными тегами плюс комментарий.

Может ли кто-нибудь помочь мне просто получить "ЭТО МОЙ ТЕКСТ" из этого?

4b9b3361

Ответ 1

Подробнее о том, как перемещаться по с помощью дерева синтаксического анализа в BeautifulSoup. Дерево обработки имеет tags и NavigableStrings (как ЭТО ТЕКСТ). Пример

from BeautifulSoup import BeautifulSoup 
doc = ['<html><head><title>Page title</title></head>',
       '<body><p id="firstpara" align="center">This is paragraph <b>one</b>.',
       '<p id="secondpara" align="blah">This is paragraph <b>two</b>.',
       '</html>']
soup = BeautifulSoup(''.join(doc))

print soup.prettify()
# <html>
#  <head>
#   <title>
#    Page title
#   </title>
#  </head>
#  <body>
#   <p id="firstpara" align="center">
#    This is paragraph
#    <b>
#     one
#    </b>
#    .
#   </p>
#   <p id="secondpara" align="blah">
#    This is paragraph
#    <b>
#     two
#    </b>
#    .
#   </p>
#  </body>
# </html>

Для перемещения вниз по дереву синтаксического анализа у вас есть contents и string.

  • Содержимое - это упорядоченный список объектов Tag и NavigableString содержащихся в элементе страницы

  • если тег имеет только один дочерний элемент node, а этот дочерний элемент node является строкой, дочерний элемент node доступен как tag.string, а также tag.contents [0]

Для вышеизложенного, то есть вы можете получить

soup.b.string
# u'one'
soup.b.contents[0]
# u'one'

Для нескольких дочерних узлов вы можете иметь, например,

pTag = soup.p
pTag.contents
# [u'This is paragraph ', <b>one</b>, u'.']

, поэтому здесь вы можете играть с contents и получать содержимое по нужному вам индексу.

Вы также можете перебирать тег, это ярлык. Например,

for i in soup.body:
    print i
# <p id="firstpara" align="center">This is paragraph <b>one</b>.</p>
# <p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>

Ответ 2

Вы можете использовать .contents:

>>> for hit in soup.findAll(attrs={'class' : 'MYCLASS'}):
...     print hit.contents[6].strip()
... 
THIS IS MY TEXT

Ответ 3

Используйте .children вместо:

from bs4 import NavigableString, Comment
print ''.join(unicode(child) for child in hit.children 
    if isinstance(child, NavigableString) and not isinstance(child, Comment))

Да, это немного танца.

Вывод:

>>> for hit in soup.findAll(attrs={'class' : 'MYCLASS'}):
...     print ''.join(unicode(child) for child in hit.children 
...         if isinstance(child, NavigableString) and not isinstance(child, Comment))
... 




      THIS IS MY TEXT

Ответ 4

с вашим собственным объектом супа:

soup.p.next_sibling.strip()
  • вы захватываете <p> непосредственно с soup.p * (это зависит от того, что он является первым <p> в дереве разбора)
  • затем используйте next_sibling для объекта тега, который возвращает soup.p, поскольку желаемый текст вложен на тот же уровень дерева разбора, что и <p>
  • .strip() - это просто метод Python str для удаления начальных и конечных пробелов.

* иначе find элемент, используя ваш выбор filter (ы)

в интерпретаторе это выглядит примерно так:

In [4]: soup.p
Out[4]: <p>something</p>

In [5]: type(soup.p)
Out[5]: bs4.element.Tag

In [6]: soup.p.next_sibling
Out[6]: u'\n      THIS IS MY TEXT\n      '

In [7]: type(soup.p.next_sibling)
Out[7]: bs4.element.NavigableString

In [8]: soup.p.next_sibling.strip()
Out[8]: u'THIS IS MY TEXT'

In [9]: type(soup.p.next_sibling.strip())
Out[9]: unicode

Ответ 5

Короткий ответ: soup.findAll('p')[0].next

Реальный ответ: вам нужна инвариантная контрольная точка, из которой вы можете добраться до своей цели.

Вы упомянули в своем комментарии к Хайдро, что текст, который вы хотите, не всегда находится в одном месте. Найдите смысл, в котором он находится в одном и том же месте относительно некоторого элемента. Затем выясните, как сделать BeautifulSoup перемещаться по дереву разбора, следуя этому инвариантному пути.

Например, в HTML, который вы указываете в исходном сообщении, целевая строка появляется сразу после элемента первого абзаца, и этот абзац не пуст. Поскольку findAll('p') найдет элементы абзаца, soup.find('p')[0] будет элементом первого абзаца.

В этом случае вы можете использовать soup.find('p'), но soup.findAll('p')[n] является более общим, поскольку, возможно, вашему фактическому сценарию нужен 5-й параграф или что-то в этом роде.

Атрибут поля next будет следующим анализируемым элементом в дереве, включая дочерние элементы. Таким образом, soup.findAll('p')[0].next содержит текст абзаца, а soup.findAll('p')[0].next.next вернет вашу цель в предоставленном HTML.

Ответ 6

Документация BeautifulSoup дает пример об удалении объектов из документа с использованием метода extract. В следующем примере цель состоит в том, чтобы удалить все комментарии из документа:

Удаление элементов

Как только у вас есть ссылка на элемент, вы можете вырвать его из дерево с методом извлечения. Этот код удаляет все комментарии из документа:

from BeautifulSoup import BeautifulSoup, Comment
soup = BeautifulSoup("""1<!--The loneliest number-->
                    <a>2<!--Can be as bad as one--><b>3""")
comments = soup.findAll(text=lambda text:isinstance(text, Comment))
[comment.extract() for comment in comments]
print soup
# 1
# <a>2<b>3</b></a>

Ответ 7

soup = BeautifulSoup(html)
for hit in soup.findAll(attrs={'class' : 'MYCLASS'}):
  hit = hit.text.strip()
  print hit

Это будет печатать: ЭТО МОЙ ТЕКСТ Попробуйте это.