如何使用自定义<comment>元素替换HTML注释

use*_*633 5 html python regex xml beautifulsoup

我正在使用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 -->
Run Code Online (Sandbox Code Playgroud)

我想出了如何找到doctype并用标签替换它<doctype>...</doctype>,但评论给了我很多挫折感.我想用HTML替换HTML注释<comment>...</comment>.在这个示例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))
Run Code Online (Sandbox Code Playgroud)

这是我第一次使用BeautifulSoup.如何使用BeautifulSoup查找和替换<comment>标签的所有HTML注释?

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

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

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

Zer*_*eus 4

你有几个问题:

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

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

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

    本来就必须是

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

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

  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)
Run Code Online (Sandbox Code Playgroud)

该代码首先迭代调用的结果find_all(),利用我们可以传递函数作为text参数的事实。在 BeautifulSoup 中,Comment是 的子类NavigableString,所以我们像搜索字符串一样搜索它,而 thelambda ...只是例如的简写

def is_comment(e):
    return isinstance(e, bs4.Comment)

soup.find_all(text=is_comment)
Run Code Online (Sandbox Code Playgroud)

然后,我们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>
Run Code Online (Sandbox Code Playgroud)