Búsqueda completa de datos XML con Python: mejores prácticas, pros y contras

Tarea

Quiero usar Python para hacer búsquedas de texto completo de datos XML.

Ejemplo de datos

 some element some other element some element  other nested element    

Funcionalidad basica

La funcionalidad más básica que quiero es que la búsqueda de “otro” en un XPath (“/ elements / elem”) devuelva al menos el valor del atributo ID para el elemento correspondiente (elem 2) y el elemento nested (elem 3, nested 1) o los XPaths coincidentes.

Funcionalidad ideal

La solución debe ser flexible y escalable. Estoy buscando posibles combinaciones de estas características:

  • buscar elementos nesteds (profundidad infinita)
  • atributos de búsqueda
  • búsqueda de oraciones y párrafos
  • buscar usando comodines
  • buscar utilizando coincidencia difusa
  • devolver información precisa de coincidencia
  • buena velocidad de búsqueda para grandes archivos XML

Pregunta

No espero una solución con todas las funcionalidades ideales, tendré que combinar diferentes funcionalidades existentes y codificar el rest yo mismo. Pero primero me gustaría saber más acerca de lo que hay por ahí, qué bibliotecas y enfoques usualmente usaría para esto, cuáles son sus pros y sus contras.

EDIT: Gracias por las respuestas hasta el momento, agregué detalles y comencé una recompensa .

No estoy seguro de si eso será suficiente para sus necesidades, pero lxml tiene soporte para expresiones regulares en xpath (es decir, puede usar xpath 1.0 más las funciones de extensión EXSLT para expresiones regulares )

En comparación con la lista de características que se agregó más tarde:

  • buscar elementos nesteds (profundidad infinita): si
  • atributos de búsqueda: sí
  • búsqueda de frases y párrafos: no. Suponiendo que los “párrafos” son elementos xml reales, entonces sí. Pero las “oraciones” como tales, no.
  • búsqueda usando comodines: sí (expresiones regulares)
  • busque utilizando la coincidencia aproximada: no (suponiendo que se derive, sinónimos, etc.)
  • devolver información de coincidencia precisa: sí
  • buena velocidad de búsqueda para archivos XML grandes: sí, excepto cuando sus archivos son tan grandes que realmente necesitaría un índice de texto completo para obtener una buena velocidad de todos modos.

La única forma de satisfacer todas las solicitudes que veo es cargar los archivos en una base de datos xml nativa que admita la búsqueda de texto real “real” (probablemente a través de XQuery Fulltext) y usarla. (¿No puedo ayudarte mucho más con eso, tal vez Sedna , que parece tener una API de python y parece ser compatible con la búsqueda de texto completo?)

Creo que sería mejor que te sirvan utilizando un motor de búsqueda de texto completo como Solr: http://lucene.apache.org/solr/

Lo que puede hacer es almacenar un “documento” en solr para cada en su xml. Puede almacenar cualquier dato que desee en el documento. Luego puede buscar en el índice y capturar el campo de id almacenado en los documentos correspondientes. Esto será muy rápido para un gran conjunto de documentos.

 select="/elements/elem//[contains(.,'other')]" 

vea también xpath: encuentre un nodo que tenga un atributo dado cuyo valor contenga una cadena

Yo recomendaría los siguientes dos:

Utilice XPath 2.0 . Soporta expresiones regulares .

O,

Use XQuery y XPath (2.0) de texto completo , que tiene características aún más potentes.

Entonces, recientemente tuve que crear un convertidor de XML a JSON. No se ajusta exactamente al estándar JSON, pero se acerca bastante. La función xml2json devuelve una representación de diccionario del objeto xml. Todos los atributos del elemento se incluyen en un diccionario con una clave de atributos y el texto del elemento se incluye en la clave de texto.

Por ejemplo, su objeto xml se vería así después de su conversión:

 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'}}, ]} 

Aquí está la función 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, {}) 

Aquí está la función 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 

La función de búsqueda que he creado después de leer tu pregunta. No ha sido 100% probado y probablemente tiene algunos errores, pero creo que sería un buen comienzo para ti. En cuanto a lo que está buscando, busca elementos nesteds, atributos, usando comodines. También devuelve el id de los elementos.

Puede usar la función como tal, donde xml es el objeto xml para buscar y reg es una cadena de patrón de expresiones regulares para buscar, por ejemplo: ‘otro’, ‘oth. *’, ‘. la. ‘Encontrarán todos los elementos con “otro” en ellos.

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

Los resultados serán una lista de diccionarios.

Espero eso ayude.

Para los archivos grandes y solos a los que se accede mediante un script de Python, debe ver que Xapian es un motor de búsqueda e indexación de texto completo con todas las funciones, maduro y robusto, y que tiene maravillosos enlaces de Python de primera clase. Funciona con Python como se escribió en Python, no hay servidores externos para ejecutar ni ninguna estupidez como esa.

Si no necesita conservar los índices y puede utilizar la base de datos en la memoria, será extremadamente rápido. Es más rápido que las soluciones basadas en Lucene y utiliza una pequeña fracción de los recursos.