我该如何使用BeautifulSoup从HTML中去除注释标签?

18

我一直在使用BeautifulSoup,感觉非常好。我的最终目标是尝试从页面中获取文本。我只是想从body中获取文本,同时特殊处理一下<a>或者<img>标签的title和/或alt属性。

到目前为止,我有以下代码:

soup = BeautifulSoup(page)
comments = soup.findAll(text=lambda text:isinstance(text, Comment))
[comment.extract() for comment in comments]
page = ''.join(soup.findAll(text=True))
page = ' '.join(page.split())
print page

1)对于我的特殊情况,您建议最好的方法是什么,以避免从我列出的两个标签中排除这些属性?如果这样做过于复杂,那么做#2就不那么重要了。

2)我想剥离<!-- --> 标签和其中的所有内容。我该怎么做?

问题编辑 @jathanism:这里有一些注释标签,我尝试过去掉它们,但即使我使用您的示例,它们仍然存在。

<!-- Begin function popUp(URL) { day = new Date(); id = day.getTime(); eval("page" + id + " = window.open(URL, '" + id + "', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=300,height=330,left = 774,top = 518');"); } // End -->
<!-- var MenuBar1 = new Spry.Widget.MenuBar("MenuBar1", {imgDown:"SpryAssets/SpryMenuBarDownHover.gif", imgRight:"SpryAssets/SpryMenuBarRightHover.gif"}); //--> <!-- var MenuBar1 = new Spry.Widget.MenuBar("MenuBar1", {imgDown:"SpryAssets/SpryMenuBarDownHover.gif", imgRight:"SpryAssets/SpryMenuBarRightHover.gif"}); //--> <!-- var whichlink=0 var whichimage=0 var blenddelay=(ie)? document.images.slide.filters[0].duration*1000 : 0 function slideit(){ if (!document.images) return if (ie) document.images.slide.filters[0].apply() document.images.slide.src=imageholder[whichimage].src if (ie) document.images.slide.filters[0].play() whichlink=whichimage whichimage=(whichimage<slideimages.length-1)? whichimage+1 : 0 setTimeout("slideit()",slidespeed+blenddelay) } slideit() //-->

你是否有一个作为测试用例的源文件?如果您能提供一些您考虑作为比较基础的东西,那将非常有帮助。 - jathanism
4个回答

65

直接从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>

我不知道为什么我没有看到那个。谢谢你提醒我! - Nathan
5
不错。但是使用带有副作用的列表推导看起来有点棘手:p。那么用map(lambda x: x.extract(), comments)怎么样? - Katriel
我仍在努力弄清楚为什么它无法查找和剥离类似于 <!-- //--> 的标签。这些反斜杠导致某些标签被忽略。 - Nathan
1
BeautifulSoup 有什么变化吗?我尝试了3.2.0版本,对于 <!-- //--> 这样的注释没有问题。 - Kiran Jonnalagadda

3
我仍在努力弄清楚为什么它无法找到和剥离像这样的标记:<!-- //-->。那些反斜杠会导致某些标记被忽略。
这可能是底层SGML解析器的问题:请参见http://www.crummy.com/software/BeautifulSoup/documentation.html#Sanitizing%20Bad%20Data%20with%20Regexps。您可以通过使用markupMassage正则表达式来覆盖它,直接从文档中获取:
import re, copy

myMassage = [(re.compile('<!-([^-])'), lambda match: '<!--' + match.group(1))]
myNewMassage = copy.copy(BeautifulSoup.MARKUP_MASSAGE)
myNewMassage.extend(myMassage)

BeautifulSoup(badString, markupMassage=myNewMassage)
# Foo<!--This comment is malformed.-->Bar<br />Baz

2
这是一个棘手的问题,看起来这是一个不错的解决方法。可惜它仍然使用正则表达式来解析HTML。该死的正则表达式! - jathanism
好的,我会处理re.compile以检测我列出的混乱注释。不过需要再熟悉一下正则表达式。唉。 - Nathan
@jathanism -- BeautifulSoup在将HTML传递给sgmllib之前会使用几个正则表达式来进行内部处理。虽然不是很美观,但也不会像Lovecraft小说中描述的那样。 - Katriel
4
仅更新此旧帖,BeautifulSoup.MARKUP_MASSAGE已被弃用。“BeautifulSoup构造函数不再识别markupMassage参数。现在由解析器负责正确处理标记。”http://www.crummy.com/software/BeautifulSoup/bs4/doc/(在页面底部) - Timber

0

如果你在寻找BeautifulSoup版本3的解决方案 BS3 文档 - Comment

soup = BeautifulSoup("""Hello! <!--I've got to be nice to get what I want.-->""")
comment = soup.find(text=re.compile("if"))
Comment=comment.__class__
for element in soup(text=lambda text: isinstance(text, Comment)):
    element.extract()
print soup.prettify()

0
如果变异不是你的菜,你可以
[t for t in soup.find_all(text=True) if not isinstance(t, Comment)]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接