【问题标题】:Highlighting glossary terms inside a HTML document突出显示 HTML 文档中的词汇表术语
【发布时间】:2012-01-12 02:34:04
【问题描述】:

我们有一个包含多达 2000 个术语的词汇表(其中每个词汇表术语可能 由一个、两个或三个单词组成(用空格分隔 或破折号)。

现在我们正在寻找一种解决方案来突出显示 (更长的)HTML 文档(最多 100 KB 的 HTML 标记),以便 生成带有突出显示的术语的静态 HTML 页面。

一个可行的解决方案的限制是:大量的词汇表术语 和长 HTML 文档……高效解决方案的蓝图是什么 (在 Python 中)。

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

客户端(浏览器)动态突出显示不是一个选项,因为 IE 会抱怨长时间运行的脚本会出现脚本超时......因此无法用于生产用途。

有更好的主意吗?

【问题讨论】:

  • 您可能想查看漂亮的汤以进行解析
  • 我发表了评论但没有回答的事实应该指出,我可能有足够的时间来实际写出正确的答案。见下文。
  • 您不必在一个循环中使用客户端高亮显示。使用 setTimeout 模拟协程。

标签: javascript python highlighting glossary glossaries


【解决方案1】:

我认为使用客户端 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')
    

    这应该用“b”替换任何出现的“a”,但留下的内容是:
    - 内联 javascript 或 css 内部(尽管内联 JS 或 CSS 不应出现在文档的正文中)。
    - 标签中的引用,例如 img、a...
    - 标签本身

    当然,根据你的词汇表,你需要确保你不会只用其他东西替换单词的一部分;为此,使用 regex insted of content.replace 是有意义的。

    【讨论】:

      【解决方案3】:

      如何浏览词汇表中的每个术语,然后对于每个术语,使用正则表达式查找 HTML 中的所有匹配项?您可以将这些事件中的每一个替换为包含在跨度中的术语,该术语具有“突出显示”的类,该类将被设置为具有背景颜色。

      【讨论】:

      • 那么,如果词汇表术语在页面标题或元数据(这是可以预料的)中,或者更糟糕的是,如果文档是关于 html 本身并说“表单”是词汇表术语,该怎么办?
      • 好点。正则表达式可以通过不考虑“”中包含的任何内容来解释元素。
      • 让我为坚持道歉,但使用正则表达式解析 HTML 是 a bad idea。更重要的是,除非你的问题非常简单(这里可能是也可能不是),除非你是一个正则表达式大师,否则你可能不会做对。解析 HTML 一个已解决的问题,因此您应该使用正确的工具来做到这一点。
      • 好的。我只是提出一个建议,而不是特别支持它作为最佳解决方案或任何东西。我自己不是正则表达式大师,所以我什至不确定我是否能用这种方法做对。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-08-29
      • 1970-01-01
      • 2012-01-13
      • 2012-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多