【问题标题】:Full text searching XML data with Python: best practices, pros & cons使用 Python 全文搜索 XML 数据:最佳实践、优缺点
【发布时间】:2011-08-13 01:16:57
【问题描述】:

任务

我想使用 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>

基本功能

我想要的最基本功能是在 XPath (“/elements/elem”) 中搜索“其他”返回至少匹配元素的 ID 属性值 (elem 2) 和嵌套元素(elem 3,嵌套 1)或匹配的 XPath。

理想的功能

解决方案应该灵活且可扩展。我正在寻找这些功能的可能组合:

  • 搜索嵌套元素(无限深度)
  • 搜索属性
  • 搜索句子和段落
  • 使用通配符搜索
  • 使用模糊匹配搜索
  • 返回精确匹配信息
  • 大型 XML 文件的搜索速度很好

问题

我不期望一个具有所有理想功能的解决方案,我必须结合不同的现有功能并自己编写其余功能。但首先我想了解更多关于那里有什么,您通常会使用哪些库和方法,它们的优缺点是什么。

编辑:感谢到目前为止的回答,我添加了细节并开始赏金

【问题讨论】:

  • @Dimitre:全文也有评分。
  • Lxml 真的是唯一的出路。
  • 好问题,+1。请参阅我的答案,了解两种最强大和最标准化的解决方案。

标签: python xml search xpath full-text-search


【解决方案1】:

不确定这是否足以满足您的需求,但lxml 支持regular expressions in xpath(意思是:您可以使用xpath 1.0 加上EXSLT extension functions for regular expressions

与后来添加的功能列表相比:

  • 搜索嵌套元素(无限深度):是
  • 搜索属性:是
  • 搜索句子和段落:没有。假设“段落”是实际的 xml 元素,那么是的。但是“句子”本身,没有。
  • 使用通配符搜索:是(正则表达式)
  • 使用模糊匹配搜索:否(假设词干、同义词等...)
  • 返回精确匹配信息:是
  • 大型 XML 文件的良好搜索速度:是的,除非您的文件非常大以至于您实际上需要全文索引才能获得良好的速度。

我看到的满足您所有请求的唯一方法是将您的文件加载到支持“真实”全文搜索(可能通过 XQuery Fulltext)的本机 xml 数据库中并使用它。 (对此无法进一步帮助您,也许是Sedna,它似乎有一个python API,似乎支持全文搜索?)

【讨论】:

  • @Jakob Bowyer:lxml 很好,但为什么它是唯一的出路?它与其他答案中的选项相比如何?
  • @locodesportif:例如,它没有接近 XPath 2.0。
【解决方案2】:

我认为你最好使用像 Solr 这样的全文搜索引擎:http://lucene.apache.org/solr/

您可以做的是在 solr 中为您的 xml 中的每个 &lt;elem /&gt; 存储一个“文档”。您可以在文档中存储您喜欢的任何数据。然后您可以搜索索引并获取存储在匹配文档中的id 字段。对于大量文档,这将非常快。

【讨论】:

  • Solr 的开销是多少?是否有任何 Python 集成?
  • Solr 是一个索引和搜索服务器。至于开销,这取决于你的意思。如果您想搜索大量此类文档,使用全文搜索引擎确实是您的最佳选择。如果你只有几个,那可能不值得。使用 Solr,您必须运行一个服务器,指定文档的格式,并将它们发送给它以进行索引。有 python 集成,但它也是一个用 JSON 响应的简单 api,因此您可以在任何地方使用它。我强烈推荐它!
  • 谢谢 Max,我会记住这一点。但我需要一个更简单、更易于部署的搜索解决方案,用于通过 Python 脚本访问的单个大型 XML 文件。
  • Solr 是 Lucence 的扩展,它是 Java。像这些要求这样简单的事情需要巨大的空间和复杂性。
  • 我不同意。他的理想要求似乎比正则表达式更适合全文搜索引擎。
【解决方案3】:
select="/elements/elem//[contains(.,'other')]"

另见xpath: find a node that has a given attribute whose value contains a string

【讨论】:

  • 这有什么限制?我可以搜索包含换行符并使用通配符的文本吗?
  • contains() 只是子字符串搜索。如果您需要“通配符”,请使用matches()
【解决方案4】:

我推荐以下两个:

使用 XPath 2.0。它supports regular expressions

或者,

使用XQuery and XPath (2.0) Full Text,它的功能更加强大。

【讨论】:

    【解决方案5】:

    所以,最近我不得不创建一个 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% 的测试,可能有一些错误,但我认为这对你来说是一个好的开始。至于您要查找的内容,它使用通配符搜索嵌套元素、属性。它还返回元素的 id。

    您可以像这样使用该函数,其中 xml 是要搜索的 xml 对象,而 reg 是要搜索的正则表达式模式字符串,例如:'other', 'oth.*', '.the.' 将找到其中包含“其他”的元素。

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

    results 将是一个字典列表。

    希望对你有帮助。

    【讨论】:

    • 好主意。这也可以使用 lxml 来实现吗?怎么样?
    • 我对lxml不熟悉,如果有人知道请告诉我们。
    【解决方案6】:

    对于通过Python脚本访问的单个大文件,你应该看看Xapian它是一个功能齐全的全文索引和搜索引擎,它成熟健壮,具有精彩一流的Python绑定。它与 Python 一起工作,就像它是用 Python 编写的一样,不需要运行外部服务器或任何类似的愚蠢。

    如果您不需要持久化索引,并且可以使用内存数据库,它将非常快。它比基于 Lucene 的解决方案更快,并且只使用一小部分资源。

    【讨论】:

      猜你喜欢
      • 2010-10-29
      • 1970-01-01
      • 1970-01-01
      • 2011-07-18
      • 2015-07-24
      • 1970-01-01
      • 2015-09-25
      • 1970-01-01
      • 2017-01-15
      相关资源
      最近更新 更多