Полнотекстовый поиск данных XML с помощью Python: лучшие практики, плюсы и минусы - PullRequest
13 голосов
/ 26 апреля 2011

Задача

Я хочу использовать Python для полнотекстового поиска по данным XML.

Пример данных

<elements>
  <elem id="1">some element</elem>
  <elem id="2">some other element</elem>
  <elem id="3">some element
    <nested id="1">
    other nested element
    </nested>
  </elem>
</elements>

Базовая функциональность

Самая базовая функциональность, которую я хочу, заключается в том, что поиск «other» в XPath («/ elements / elem») возвращает как минимум значение атрибута ID для соответствующего элемента (элемент 2) и вложенного элемента (элемент 3, вложенный 1) или соответствующих XPath.

Идеальная функциональность

Решение должно быть гибким и масштабируемым.Я ищу возможные комбинации этих функций:

  • поиск вложенных элементов (бесконечная глубина)
  • поиск атрибутов
  • поиск предложений и абзацев
  • поиск с использованием подстановочных знаков
  • поиск с использованием нечеткого соответствия
  • возврат точной информации о соответствии
  • хорошая скорость поиска для больших файлов XML

Вопрос

Я не ожидаю решения со всеми идеальными функциями, мне придется комбинировать различные существующие функции и самостоятельно кодировать все остальное.Но сначала я хотел бы узнать больше о том, что есть, какие библиотеки и подходы вы обычно используете для этого, каковы их плюсы и минусы.

РЕДАКТИРОВАТЬ: Спасибо за ответы, я добавилподробно и началось вознаграждение .

Ответы [ 6 ]

6 голосов
/ 27 апреля 2011

Не уверен, что этого будет достаточно для ваших нужд, но lxml имеет поддержку регулярных выражений в xpath (что означает: вы можете использовать xpath 1.0 плюс расширение EXSLTфункции для регулярных выражений )

По сравнению со списком функций, который был добавлен позже:

  • поиск вложенных элементов (бесконечная глубина): да
  • поиск атрибутов: да
  • поиск предложений и абзацев: нет.Предполагая, что «абзацы» являются фактическими элементами XML, тогда да.Но «предложения» как таковые, нет.
  • поиск с использованием подстановочных знаков: да (регулярные выражения)
  • поиск с использованием нечеткого сопоставления: нет (при условии, что в основе есть синонимы и т. Д.)
  • возвращает точную информацию о соответствии: да
  • хорошая скорость поиска для больших файлов XML: да, за исключением случаев, когда ваши файлы настолько велики, что вам в любом случае потребуется полнотекстовый индекс для получения хорошей скорости.

Единственный способ удовлетворить все ваши запросы, которые я вижу, - это загрузить ваши файлы в собственную базу данных xml, которая поддерживает «настоящий» полнотекстовый поиск (возможно, через XQuery Fulltext) и использовать его.(не могу вам в этом помочь, может быть, Sedna , который, кажется, имеет API-интерфейс Python и поддерживает полнотекстовый поиск?)

2 голосов
/ 26 апреля 2011

Я думаю, вам лучше всего использовать систему полнотекстового поиска, такую ​​как Solr: http://lucene.apache.org/solr/

Что вы можете сделать, это сохранить «документ» в solr для каждого <elem /> в вашем XML. Вы можете хранить любые данные, которые вам нравятся, в документе. Затем вы можете выполнить поиск по индексу и получить поле id, хранящееся в соответствующих документах. Это будет очень быстро для большого набора документов.

1 голос
/ 05 мая 2011

Итак, недавно мне пришлось создать конвертер XML в JSON. Он не совсем соответствует стандарту JSON, но подходит довольно близко. Функция xml2json возвращает словарное представление объекта xml. Все атрибуты элемента включены в словарь с ключом атрибутов, а текст элемента включен в текстовый ключ.

Например, ваш xml-объект будет выглядеть после преобразования:

json = {'elements': 
    {'elem': [
        {'attributes': {'id', '1'}, 'text': 'some element'},
        {'attributes': {'id', '2'}, 'text': 'some other element'},
        {'attributes': {'id', '3'}, 'text': 'some element', 'nested': {
            'attributes': {'id', '1'}, 'text': 'other nested element'}},
    ]}

Вот функция xml2json.

def xml2json(x):
    def get_attributes(atts):
        atts = dict(atts)
        d = {}
        for k, v in atts.items():
            d[k] = v.value
        return d

    def get_children(n, d):
        tmp = {}
        d.setdefault(n.nodeName, {})
        if n.attributes:
            tmp['attributes'] = get_attributes(n.attributes)
        if n.hasChildNodes():
            for c in n.childNodes:
                if c.nodeType == c.TEXT_NODE or c.nodeName == '#cdata-section':
                    tmp['text'] = c.data
                else:
                    children = get_children(c, {})
                    for ck, cv in children.items():
                        if ck in d[n.nodeName]:
                            if not isinstance(d[n.nodeName][ck], list):
                                tmpv = d[n.nodeName][ck]
                                d[n.nodeName][ck] = []
                                d[n.nodeName][ck].append(tmpv)
                            d[n.nodeName][ck].append(cv)
                        else:
                            d[n.nodeName][ck] = cv

        for tk, tv in tmp.items():
            d[n.nodeName][tk] = tv

        return d

    return get_children(x.firstChild, {})

Вот функция searchjson.

def searchjson(sobj, reg):
    import re
    results = []
    if isinstance(sobj, basestring):
        # search the string and return the output
        if re.search(reg, sobj):
            results.append(sobj)
    else:
        # dictionary
        for k, v in sobj.items():
            newv = v
            if not isinstance(newv, list):
                newv = [newv]

            for elem in newv:
                has_attributes = False
                if isinstance(elem, dict):
                    has_attributes = bool(elem.get('attributes', False))
                res = searchjson(elem, reg)
                res = [] if not res else res
                for r in res:
                    r_is_dict = isinstance(r, dict)
                    r_no_attributes = r_is_dict and 'attributes' not in r.keys()
                    if has_attributes and r_no_attributes :
                        r.update({'attributes': elem.get('attributes', {})})

                    results.append({k: r})

    return results

Функция поиска, которую я создал после прочтения вашего вопроса. Он не был проверен на 100% и, возможно, содержит несколько ошибок, но я думаю, что это будет хорошим началом для вас. Что касается того, что вы ищете, он ищет вложенные элементы, атрибуты, используя подстановочные знаки. Он также возвращает идентификатор элементов.

Вы можете использовать эту функцию следующим образом: где xml - это объект xml для поиска, а reg - строка шаблона регулярного выражения для поиска, например: 'other', 'oth. *', '. the. 'все найдут элементы с "другим" в них.

json = xml2json(xml)
results = searchjson(json, reg='other')

Результатами будет список словарей.

Надеюсь, это поможет.

1 голос
/ 28 апреля 2011

Я бы порекомендовал следующие два:

Использовать XPath 2.0 .Он поддерживает регулярные выражения .

Или

Использование XQuery и XPath (2.0) Полный текст , который обладает еще более мощными функциями.

1 голос
/ 26 апреля 2011
0 голосов
/ 28 апреля 2011

Для отдельных больших файлов, к которым обращается скрипт Python, вы должны взглянуть на Xapian - это полнофункциональный механизм полнотекстового индексирования и поиска, который является зрелым и надежным и имеет чудесный привязки первого класса Python.Он работает с Python, как это было написано на Python, не требует запуска внешних серверов или какой-либо глупости.

Если вам не нужно сохранять индексы, и вы можете использовать базу данных в памяти, это будет чрезвычайнобыстро.Это быстрее, чем решения на основе Lucene, и использует небольшую часть ресурсов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...