如何用自定义 <comment> 元素替换 HTML 注释
Posted
技术标签:
【中文标题】如何用自定义 <comment> 元素替换 HTML 注释【英文标题】:How to replace HTML comments with custom <comment> elements 【发布时间】:2015-04-19 17:58:03 【问题描述】:我正在使用 Python 中的 BeautifulSoup 将大量 html 文件大量转换为 XML。
示例 HTML 文件如下所示:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- this is an HTML comment -->
<!-- this is another HTML comment -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
...
<!-- here is a comment inside the head tag -->
</head>
<body>
...
<!-- Comment inside body tag -->
<!-- Another comment inside body tag -->
<!-- There could be many comments in each file and scattered, not just 1 in the head and three in the body. This is just a sample. -->
</body>
</html>
<!-- This comment is the last line of the file -->
我想出了如何找到文档类型并将其替换为标签<doctype>...</doctype>
,但评论让我很沮丧。我想用<comment>...</comment>
替换HTML cmets。在这个 HTML 示例中,我能够替换前两个 HTML cmets,但 html
标记内的任何内容和结束 html 标记之后的最后一个注释我都没有。
这是我的代码:
file = open ("sample.html", "r")
soup = BeautifulSoup(file, "xml")
for child in soup.children:
# This takes care of the first two HTML comments
if isinstance(child, bs4.Comment):
child.replace_with("<comment>" + child.strip() + "</comment>")
# This should find all nested HTML comments and replace.
# It looks like it works but the changes are not finalized
if isinstance(child, bs4.Tag):
re.sub("(<!--)|(<!--)", "<comment>", child.text, flags=re.MULTILINE)
re.sub("(-->)|(--&gr;)", "</comment>", child.text, flags=re.MULTILINE)
# The HTML comments should have been replaced but nothing changed.
print (soup.prettify(formatter=None))
这是我第一次使用 BeautifulSoup。如何使用 BeautifulSoup 查找并替换所有带有 <comment>
标签的 HTML cmets?
我可以通过pickle
将其转换为字节流,对其进行序列化,应用正则表达式,然后将其反序列化回BeautifulSoup
对象吗?这会起作用还是只会导致更多问题?
我尝试在子标记对象上使用pickle,但反序列化失败并出现TypeError: __new__() missing 1 required positional argument: 'name'
。
然后我尝试通过child.text
仅腌制标签的文本,但由于AttributeError: can't set attribute
,反序列化失败。基本上,child.text
是只读的,这就解释了为什么正则表达式不起作用。所以,我不知道如何修改文本。
【问题讨论】:
应用所有更改后不会导致格式错误的xml
文件吗?
我不知道,但Chilkat
有一个(不是免费的)HTML-to-XML 转换 python 库,可以将所有 HTML cmets 转换为 <comment>
,并且 XML 文件看起来不错。
【参考方案1】:
你有几个问题:
您不能修改child.text
。它是一个只读属性,只是在幕后调用get_text()
,其结果是一个全新的字符串,未连接到您的文档。
re.sub()
不会就地修改任何内容。你的线路
re.sub("(<!--)|(<!--)", "<comment>", child.text, flags=re.MULTILINE)
应该是
child.text = re.sub("(<!--)|(<!--)", "<comment>", child.text, flags=re.MULTILINE)
...但由于第 1 点,这无论如何都行不通。
尝试通过用正则表达式替换文档中的文本块来修改文档是使用 BeautifulSoup 的错误方法。相反,您需要找到节点并将其替换为其他节点。
这是一个可行的解决方案:
import bs4
with open("example.html") as f:
soup = bs4.BeautifulSoup(f)
for comment in soup.find_all(text=lambda e: isinstance(e, bs4.Comment)):
tag = bs4.Tag(name="comment")
tag.string = comment.strip()
comment.replace_with(tag)
这段代码首先遍历调用find_all()
的结果,利用我们可以将pass a function 作为text
参数这一事实。在 BeautifulSoup 中,Comment
是 NavigableString
的子类,因此我们将其视为字符串进行搜索,而 lambda ...
只是例如的简写
def is_comment(e):
return isinstance(e, bs4.Comment)
soup.find_all(text=is_comment)
然后,我们创建一个新的Tag
,命名为适当的名称,将其内容设置为原始评论的剥离内容,并将评论替换为我们刚刚创建的标签。
结果如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<comment>this is an HTML comment</comment>
<comment>this is another HTML comment</comment>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
...
<comment>here is a comment inside the head tag</comment>
</head>
<body>
...
<comment>Comment inside body tag</comment>
<comment>Another comment inside body tag</comment>
<comment>There could be many comments in each file and scattered, not just 1 in the head and three in the body. This is just a sample.</comment>
</body>
</html>
<comment>This comment is the last line of the file</comment>
【讨论】:
非常感谢您的帮助。我现在正在阅读这个。如果可以,请您解释或详细说明python代码中的每一行,以for
开头。再说一次,我对 Beautiful Soup 还是很陌生。先感谢您!我对 lamda 的用法一点也不熟悉。
@user3621633 我已经添加了对代码现在功能的解释。
这成功了!谢谢你。我唯一要添加的是我需要 XML 格式的。所以,我想我必须在写入输出文件之前将 BeautifulSoup 与 xml 一起应用。如果我第一次使用 xml,HTML cmets 会以 HTML 实体(即 <)的形式出现,我不确定 BeautifulSoup 是否可以使用这些实体。以上是关于如何用自定义 <comment> 元素替换 HTML 注释的主要内容,如果未能解决你的问题,请参考以下文章