在HTML文档中突出显示术语

1 投票
3 回答
796 浏览
提问于 2025-04-17 07:36

我们有一个词汇表,里面有多达2000个词条(每个词条可能由一个、两个或三个单词组成,这些单词之间可以用空格或连字符分开)。

现在,我们想找到一种方法,在一个较长的HTML文档中(最多100KB的HTML标记)突出显示所有的词条,以便生成一个包含高亮词条的静态HTML页面。

要实现这个功能,我们面临的挑战是:词汇表的词条数量很大,而且HTML文档也很长……那么,如何在Python中设计一个高效的解决方案呢?

目前我在考虑使用lxml来解析HTML文档,遍历所有的文本节点,然后将每个文本节点的内容与所有词汇表中的词条进行匹配。

在客户端(浏览器)实时高亮显示是不行的,因为IE会对长时间运行的脚本发出警告,导致脚本超时……所以这种方法不适合生产环境。

有没有更好的主意呢?

3 个回答

-1

你可以先查看一下词汇表里的每一个术语,然后对每个术语使用正则表达式(regex)在HTML中查找所有出现的地方。接着,把找到的每个地方都替换成这个术语,并用一个带有“highlighted”类的标签包裹起来,这样就可以通过样式设置背景颜色来突出显示了。

0

我觉得用客户端的JavaScript来高亮显示是最好的选择。这种方法可以节省服务器的处理时间和带宽,更重要的是,它能保持HTML的整洁,方便那些不需要多余标记的人使用,比如在打印或转换成其他格式时。

为了避免超时,可以把任务分成小块,然后一个一个地处理,使用setTimeout来实现。下面是这种方法的一个例子:

function hilite(terms, chunkSize) {

    // prepare stuff

    var terms = new RegExp("\\b(" + terms.join("|") + ")\\b", "gi");

    // collect all text nodes in the document

    var textNodes = [];
    $("body").find("*").contents().each(function() {
        if (this.nodeType == 3)
            textNodes.push(this)
    });

    // process N text nodes at a time, surround terms with text "markers"

    function step() {
        for (var i = 0; i < chunkSize; i++) {
            if (!textNodes.length)
                return done();
            var node = textNodes.shift();
            node.nodeValue = node.nodeValue.replace(terms, "\x1e$&\x1f");
        }
        setTimeout(step, 100);
    }

    // when done, replace "markers" with html

    function done() {
        $("body").html($("body").html().
            replace(/\x1e/g, "<b>").
            replace(/\x1f/g, "</b>")
        );
    }

    // let's go

    step()
}

使用方法如下:

$(function() {
    hilite(["highlight", "these", "words"], 100)
})

如果你有任何问题,随时问我。

2

你可以使用一个解析器来递归地遍历你的树形结构,并只替换那些由文本组成的标签。
在这个过程中,你需要考虑几个问题:
- 不是所有的文本都需要被替换(比如,内联的JavaScript)。
- 文档中的某些元素可能不需要解析(比如,标题等)。

下面是一个简单的示例,虽然这个示例并不适合实际生产环境,但可以帮助你理解如何实现这个功能:

html = """The HTML you need to parse"""
import BeautifulSoup

IGNORE_TAGS = ['script', 'style']

def parse_content(item, replace_what, replace_with, ignore_tags = IGNORE_TAGS):
    for content in item.contents:
        if isinstance(content, BeautifulSoup.NavigableString):
            content.replaceWith(content.replace(replace_what, replace_with, ignore_tags))
        else:
            if content.name not in ignore_tags:
                parse_content(content, replace_what, replace_with, ignore_tags)
    return item

soup = BeautifulSoup.BeautifulSoup(html)
body = soup.html.body
replaced_content = parse_content(body, 'a', 'b')

这个示例应该会把所有的“a”替换成“b”,但会保留以下内容:
- 在内联JavaScript或CSS中的内容(虽然内联的JS或CSS通常不应该出现在文档的主体中)。
- 像img、a等标签中的引用。
- 标签本身。

当然,根据你的词汇表,你还需要确保不会只替换一个单词的一部分;为此,使用正则表达式会比使用content.replace更合适。

撰写回答