60

目前我有这样的代码:

soup = BeautifulSoup(value)

for tag in soup.findAll(True):
    if tag.name not in VALID_TAGS:
        tag.extract()
soup.renderContents()

除了我不想丢弃无效标签内的内容。如何在调用 soup.renderContents() 时摆脱标签但将内容保留在里面?

4

11 回答 11

81

BeautifulSoup 库的当前版本在 Tag 对象上有一个未记录的方法,称为 replaceWithChildren()。所以,你可以这样做:

html = "<p>Good, <b>bad</b>, and <i>ug<b>l</b><u>y</u></i></p>"
invalid_tags = ['b', 'i', 'u']
soup = BeautifulSoup(html)
for tag in invalid_tags: 
    for match in soup.findAll(tag):
        match.replaceWithChildren()
print soup

看起来它的行为就像您想要的那样,并且是相当简单的代码(尽管它确实通过 DOM 进行了几次传递,但这很容易被优化。)

于 2011-12-09T00:47:21.220 回答
63

我使用的策略是用它的内容替换一个标签,如果它们是类型的NavigableString,如果它们不是,然后递归到它们并用NavigableString等替换它们的内容。试试这个:

from BeautifulSoup import BeautifulSoup, NavigableString

def strip_tags(html, invalid_tags):
    soup = BeautifulSoup(html)

    for tag in soup.findAll(True):
        if tag.name in invalid_tags:
            s = ""

            for c in tag.contents:
                if not isinstance(c, NavigableString):
                    c = strip_tags(unicode(c), invalid_tags)
                s += unicode(c)

            tag.replaceWith(s)

    return soup

html = "<p>Good, <b>bad</b>, and <i>ug<b>l</b><u>y</u></i></p>"
invalid_tags = ['b', 'i', 'u']
print strip_tags(html, invalid_tags)

结果是:

<p>Good, bad, and ugly</p>

我在另一个问题上给出了同样的答案。它似乎出现了很多。

于 2010-07-12T03:25:02.597 回答
19

尽管其他人已经在评论中提到了这一点,但我想我会发布一个完整的答案,展示如何使用 Mozilla 的 Bleach 来做到这一点。就个人而言,我认为这比使用 BeautifulSoup 好得多。

import bleach
html = "<b>Bad</b> <strong>Ugly</strong> <script>Evil()</script>"
clean = bleach.clean(html, tags=[], strip=True)
print clean # Should print: "Bad Ugly Evil()"
于 2012-10-20T15:22:36.430 回答
11

我有一个更简单的解决方案,但我不知道它是否有缺点。

更新:有一个缺点,请参阅 Jesse Dhillon 的评论。此外,另一种解决方案是使用 Mozilla 的Bleach代替 BeautifulSoup。

from BeautifulSoup import BeautifulSoup

VALID_TAGS = ['div', 'p']

value = '<div><p>Hello <b>there</b> my friend!</p></div>'

soup = BeautifulSoup(value)

for tag in soup.findAll(True):
    if tag.name not in VALID_TAGS:
        tag.replaceWith(tag.renderContents())

print soup.renderContents()

这也将<div><p>Hello there my friend!</p></div>根据需要打印。

于 2009-11-20T03:43:13.957 回答
7

在删除标签之前,您可能必须将标签的子代移动为标签父代的子代——这就是您的意思吗?

如果是这样,那么,虽然在正确的位置插入内容很棘手,但这样的事情应该可以工作:

from BeautifulSoup import BeautifulSoup

VALID_TAGS = 'div', 'p'

value = '<div><p>Hello <b>there</b> my friend!</p></div>'

soup = BeautifulSoup(value)

for tag in soup.findAll(True):
    if tag.name not in VALID_TAGS:
        for i, x in enumerate(tag.parent.contents):
          if x == tag: break
        else:
          print "Can't find", tag, "in", tag.parent
          continue
        for r in reversed(tag.contents):
          tag.parent.insert(i, r)
        tag.extract()
print soup.renderContents()

使用示例值,可以<div><p>Hello there my friend!</p></div>根据需要打印。

于 2009-11-19T19:42:02.633 回答
7

你可以使用soup.text

.text 删除所有标签并连接所有文本。

于 2013-12-23T06:08:05.240 回答
3

使用展开。

展开将删除标签的多次出现之一,并仍然保留内容。

例子:

>> soup = BeautifulSoup('Hi. This is a <nobr> nobr </nobr>')
>> soup
<html><body><p>Hi. This is a <nobr> nobr </nobr></p></body></html>
>> soup.nobr.unwrap
<nobr></nobr>
>> soup
>> <html><body><p>Hi. This is a nobr </p></body></html>
于 2016-12-26T09:11:30.517 回答
2

对我来说,提议的答案似乎都不适用于 BeautifulSoup。这是一个适用于 BeautifulSoup 3.2.1 的版本,并且在连接来自不同标签的内容而不是连接单词时也会插入一个空格。

def strip_tags(html, whitelist=[]):
    """
    Strip all HTML tags except for a list of whitelisted tags.
    """
    soup = BeautifulSoup(html)

    for tag in soup.findAll(True):
        if tag.name not in whitelist:
            tag.append(' ')
            tag.replaceWithChildren()

    result = unicode(soup)

    # Clean up any repeated spaces and spaces like this: '<a>test </a> '
    result = re.sub(' +', ' ', result)
    result = re.sub(r' (<[^>]*> )', r'\1', result)
    return result.strip()

例子:

strip_tags('<h2><a><span>test</span></a> testing</h2><p>again</p>', ['a'])
# result: u'<a>test</a> testing again'
于 2013-04-22T10:04:54.247 回答
2

这是更好的解决方案,无需任何麻烦和样板代码即可过滤掉保留内容的标签。假设您想删除父标签中的任何子标签,然后只想保留内容/文本,您可以简单地执行以下操作:

for p_tags in div_tags.find_all("p"):
    print(p_tags.get_text())

就是这样,您可以自由使用父标签中的所有 br 或 ib 标签并获得干净的文本。

于 2016-09-25T17:13:35.090 回答
2

这是此函数的 python 3 友好版本:

from bs4 import BeautifulSoup, NavigableString
invalidTags = ['br','b','font']
def stripTags(html, invalid_tags):
    soup = BeautifulSoup(html, "lxml")
    for tag in soup.findAll(True):
        if tag.name in invalid_tags:
            s = ""
            for c in tag.contents:
                if not isinstance(c, NavigableString):
                    c = stripTags(str(c), invalid_tags)
                s += str(c)
            tag.replaceWith(s)
    return soup
于 2019-06-01T14:04:25.670 回答
0

这是一个老问题,但只是说一个更好的方法来做到这一点。首先,BeautifulSoup 3* 已不再开发,因此您应该使用 BeautifulSoup 4*,即所谓的 bs4

此外,lxml 具有您需要的功能:Cleaner 类具有属性remove_tags,您可以将其设置为标签,当它们的内容被拉入父标签时将被删除。

于 2015-03-12T01:51:11.947 回答