如何用自定义 <comment> 元素替换 HTML 评论

How to replace HTML comments with custom <comment> elements

我正在使用 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 评论。在这个例子 HTML 中,我能够替换前两个 HTML 评论,但是 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("(<!--)|(&lt;!--)", "<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 查找所有 HTML 评论并将其替换为 <comment> 标签?

我可以通过 pickle 将其转换为字节流,对其进行序列化,应用正则表达式,然后将其反序列化为 BeautifulSoup 对象吗?这会起作用还是只会导致更多问题?

我尝试在子标签对象上使用 pickle,但反序列化失败 TypeError: __new__() missing 1 required positional argument: 'name'

然后我尝试通过 child.text 只对标签的文本进行酸洗,但由于 AttributeError: can't set attribute,反序列化失败了。基本上,child.text 是只读的,这解释了为什么正则表达式不起作用。所以,我不知道如何修改文本。

您有几个问题:

  1. 您无法修改 child.text。它是一个只读的 属性,只是在幕后调用 get_text(),其结果是一个与您的文档无关的全新字符串。

  2. re.sub() 不会就地修改任何内容。你的线路

    re.sub("(<!--)|(&lt;!--)", "<comment>", child.text, flags=re.MULTILINE)
    

    本来应该是

    child.text = re.sub("(<!--)|(&lt;!--)", "<comment>", child.text, flags=re.MULTILINE)
    

    ...但由于第 1 点,这无论如何都行不通。

  3. 试图通过用正则表达式替换其中的文本块来修改文档是错误的使用方式 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中,CommentNavigableString的子类,所以我们把它当作字符串来查找,而lambda ...只是一个shorthand例如

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>