BeautifulSoup:剥除指定属性,但保留标签及其内容

13 投票
5 回答
19252 浏览
提问于 2025-04-17 11:17

我正在尝试把一个由MS FrontPage生成的网站的HTML进行处理,写了一个BeautifulSoup的脚本来实现这个目标。

不过,我在尝试从文档中每个包含特定属性的标签中去掉这些属性时遇到了困难。以下是我的代码片段:

REMOVE_ATTRIBUTES = ['lang','language','onmouseover','onmouseout','script','style','font',
                        'dir','face','size','color','style','class','width','height','hspace',
                        'border','valign','align','background','bgcolor','text','link','vlink',
                        'alink','cellpadding','cellspacing']

# remove all attributes in REMOVE_ATTRIBUTES from all tags, 
# but preserve the tag and its content. 
for attribute in REMOVE_ATTRIBUTES:
    for tag in soup.findAll(attribute=True):
        del(tag[attribute])

这段代码运行没有错误,但实际上并没有去掉任何属性。当我去掉外层循环,只硬编码一个属性(soup.findAll('style'=True))时,它是可以工作的。

有人知道这里的问题吗?

另外,我也不太喜欢嵌套循环。如果有人知道更简洁的写法,比如用map或filter的方式,我很想看看。

5 个回答

6

这里简单说一下问题所在:如果你把HTML属性作为关键字参数传递,那么关键字就是属性的名称。所以你的代码在寻找名称为attribute的标签,因为变量没有被展开。

这就是为什么:

  1. 直接写死属性名称可以正常工作[0]
  2. 代码不会出错,只是搜索没有找到任何标签

要解决这个问题,可以把你要找的属性作为一个dict传递:

for attribute in REMOVE_ATTRIBUTES:
    for tag in soup.find_all(attrs={attribute: True}):
        del tag[attribute]

希望这对未来的某个人有帮助,

dtk

[0]: 虽然在你的例子中需要写成find_all(style=True),没有引号,因为SyntaxError: keyword can't be an expression

7

这是一个Python 2版本的unutbu的回答:

REMOVE_ATTRIBUTES = ['lang','language','onmouseover']

doc = '''<html><head><title>Page title</title></head><body><p id="firstpara" align="center">This is <i>paragraph</i> <a onmouseout="">one</a>.<p id="secondpara" align="blah">This is <i>paragraph</i> <b>two</b>.</html>'''

soup = BeautifulSoup.BeautifulSoup(doc)

for tag in soup.recursiveChildGenerator():
    if hasattr(tag, 'attrs'):
        tag.attrs = {key:value for key,value in tag.attrs.iteritems()
                    if key not in REMOVE_ATTRIBUTES}
12

这一行

for tag in soup.findAll(attribute=True):

找不到任何的 tag。可能有办法使用 findAll,但我不太确定。

不过,这个方法可以用(从 beautifulsoup 4.8.1 开始):

import bs4
REMOVE_ATTRIBUTES = [
    'lang','language','onmouseover','onmouseout','script','style','font',
    'dir','face','size','color','style','class','width','height','hspace',
    'border','valign','align','background','bgcolor','text','link','vlink',
    'alink','cellpadding','cellspacing']

doc = '''<html><head><title>Page title</title></head><body><p id="firstpara" align="center">This is <i>paragraph</i> <a onmouseout="">one</a>.<p id="secondpara" align="blah">This is <i>paragraph</i> <b>two</b>.</html>'''
soup = bs4.BeautifulSoup.BeautifulSoup(doc)
for tag in soup.descendants:
    if isinstance(tag, bs4.element.Tag):
        tag.attrs = {key: value for key, value in tag.attrs
                     if key not in REMOVE_ATTRIBUTES}
print(soup.prettify())

这是之前的代码,可能在旧版本的 beautifulsoup 中有效:

import BeautifulSoup
REMOVE_ATTRIBUTES = [
    'lang','language','onmouseover','onmouseout','script','style','font',
    'dir','face','size','color','style','class','width','height','hspace',
    'border','valign','align','background','bgcolor','text','link','vlink',
    'alink','cellpadding','cellspacing']

doc = '''<html><head><title>Page title</title></head><body><p id="firstpara" align="center">This is <i>paragraph</i> <a onmouseout="">one</a>.<p id="secondpara" align="blah">This is <i>paragraph</i> <b>two</b>.</html>'''
soup = BeautifulSoup.BeautifulSoup(doc)
for tag in soup.recursiveChildGenerator():
    try:
        tag.attrs = [(key,value) for key,value in tag.attrs
                     if key not in REMOVE_ATTRIBUTES]
    except AttributeError: 
        # 'NavigableString' object has no attribute 'attrs'
        pass
print(soup.prettify())

请注意,这段代码只适用于 Python 3。如果你需要在 Python 2 中使用,请查看 Nóra 的回答。

撰写回答