Выделение терминов словаря в документе HTML

У нас есть глоссарий, содержащий до 2000 терминов (где каждый термин глоссария может состоять из одного, двух или трех слов (разделенных пробелами или тире).

Теперь мы ищем решение для выделения всех терминов внутри (более длинного) HTML-документа (до 100 КБ разметки HTML) для создания статической HTML-страницы с выделенными терминами.

Ограничения для рабочего решения: большое количество терминов глоссария и длинные HTML-документы... что могло бы стать основой для эффективного решения (в Python).

Сейчас я думаю о синтаксическом анализе документа HTML с использованием lxml, итерации по всем текстовым узлам, а затем сопоставлении содержимого каждого текстового узла со всеми терминами глоссария.

Подсветка на стороне клиента (в браузере) не возможна, так как IE будет жаловаться на долго выполняющиеся скрипты с таймаутом... так что их невозможно использовать для производственного использования.

Есть идея получше?

3 ответа

Вы можете использовать синтаксический анализатор для рекурсивной навигации по дереву и замены только тегов, которые сделаны из текста.
При этом есть еще несколько вещей, которые вам необходимо учитывать:
- Не весь текст необходимо заменить (напр. Встроенный 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,...
- сам тег

Конечно, вам потребуется, в зависимости от вашего глоссария, убедиться, что вы не заменяете только часть слова чем-то другим; для этого имеет смысл использовать регулярное выражение insted of content.replace.

Я думаю, что выделение с помощью клиентского 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)
})

Дайте мне знать, если у вас есть вопросы.

Как насчет прохождения каждого термина в глоссарии, а затем для каждого термина с помощью регулярных выражений найти все вхождения в HTML? Вы можете заменить каждое из этих вхождений на термин, заключенный в промежуток, с классом "выделенный", стиль которого будет иметь цвет фона.

Другие вопросы по тегам