48

我尝试从以下 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

但是我得到了所有嵌套标签之间的所有文本以及评论。

任何人都可以帮助我从中得到“这是我的文字”吗?

4

7 回答 7

50

详细了解如何BeautifulSoup. 解析树得到tagsNavigableStrings(因为这是一个文本)。一个例子

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>

要向下移动您拥有的解析树contentsstring.

  • contents 是页面元素中包含的 Tag 和 NavigableString 对象的有序列表

  • 如果标签只有一个子节点,并且该子节点是一个字符串,则该子节点作为 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>
于 2013-05-30T12:46:44.543 回答
19

改用.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
于 2013-05-30T11:59:13.373 回答
15

您可以使用.contents

>>> for hit in soup.findAll(attrs={'class' : 'MYCLASS'}):
...     print hit.contents[6].strip()
... 
THIS IS MY TEXT
于 2013-05-30T12:27:58.807 回答
11

用你自己的汤对象:

soup.p.next_sibling.strip()
  1. 你直接用 * 抓住 <p> soup.p(这取决于它是解析树中的第一个 <p> )
  2. 然后next_sibling在返回的标记对象上使用,soup.p因为所需的文本与 <p> 嵌套在解析树的同一级别
  3. .strip()只是一个删除前导和尾随空格的 Python str 方法

*否则只需使用您选择的过滤查找元素

在解释器中,这看起来像:

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
于 2014-07-18T21:05:58.200 回答
8

简短的回答:soup.findAll('p')[0].next

真正的答案:你需要一个不变的参考点,你可以从这个参考点到达你的目标。

您在对 Haidro 的回答的评论中提到,您想要的文本并不总是在同一个地方。找到一种感觉,它相对于某个元素在同一个地方。然后弄清楚如何让 BeautifulSoup 沿着那个不变的路径导航解析树。

例如,在您在原始帖子中提供的 HTML 中,目标字符串立即出现在第一个段落元素之后,并且该段落不为空。因为findAll('p')会找到段落元素,soup.find('p')[0]所以会是第一个段落元素。

在这种情况下,您可以使用soup.find('p')soup.findAll('p')[n]更通用,因为您的实际场景可能需要第 5 段或类似的内容。

field 属性将next是树中下一个解析的元素,包括子元素。Sosoup.findAll('p')[0].next包含段落的文本,并将soup.findAll('p')[0].next.next在提供的 HTML 中返回您的目标。

于 2013-05-31T03:46:28.620 回答
4
soup = BeautifulSoup(html)
for hit in soup.findAll(attrs={'class' : 'MYCLASS'}):
  hit = hit.text.strip()
  print hit

这将打印:这是我的文本试试这个..

于 2018-01-24T10:17:22.600 回答
0

BeautifulSoup 文档提供了一个关于使用 extract 方法从文档中删除对象的示例。在以下示例中,目的是从文档中删除所有注释:

移除元素

一旦你引用了一个元素,你就可以使用 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>
于 2013-05-30T13:10:09.923 回答