Python BeautifulSoup提取元素之间的文本

时间:2013-05-30 11:54:37

标签: 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

但是我得到了所有嵌套标签和评论之间的所有文字。

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

7 个答案:

答案 0 :(得分:31)

详细了解如何浏览through the parse tree in 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>

答案 1 :(得分:14)

您可以使用.contents

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

答案 2 :(得分:11)

改为使用.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

答案 3 :(得分:9)

用你自己的汤对象:

soup.p.next_sibling.strip()
  1. 你抓住&lt; p&gt;直接使用soup.p *(这取决于它是解析树中的第一个&lt; p&gt;)
  2. 然后在next_sibling返回的标记对象上使用soup.p,因为所需的文本嵌套在与&lt; p&gt;相同的解析树级别上。
  3. .strip()只是一个删除前导空格和尾随空格的Python str方法
  4. *否则仅find元素使用您选择的filter(s)

    解释器中的

    看起来像是:

    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
    

答案 4 :(得分:6)

简短回答: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中返回您的目标。

答案 5 :(得分:2)

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

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

答案 6 :(得分:0)

BeautifulSoup documentation提供了一个使用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>