代码之家  ›  专栏  ›  技术社区  ›  Sumant

支持xpath的快速python XML验证器

  •  4
  • Sumant  · 技术社区  · 15 年前

    我需要读取一个大的XML(65MB),根据XSD验证它,并对它运行xpath查询。下面,我给出了一个LXML版本。运行查询需要很长时间(超过5分钟),但验证似乎很快。

    我有几个问题。注重性能的Python程序员如何使用lxml编写程序?其次,如果LXML不适合该工作,那么还有什么?你能给我一个代码片段吗?

    import sys
    from datetime import datetime
    from lxml import etree
    
    start = datetime.now()
    schema_file = open("library.xsd")
    schema = etree.XMLSchema(file=schema_file)
    parser = etree.XMLParser(schema = schema)
    data_file = open(sys.argv[1], 'r')
    tree = etree.parse(data_file, parser)
    root = tree.getroot()
    data_file.close()
    schema_file.close()
    end = datetime.now()
    delta = end-start
    print "Parsing time = ", delta
    
    start = datetime.now()
    name_list = root.xpath("book/author/name/text()")
    print ("Size of list = " + str(len(name_list)))
    end = datetime.now()
    delta = end-start
    print "Query time = ", delta
    
    2 回复  |  直到 15 年前
        1
  •  0
  •   Andrew Walker    15 年前

    我想知道您是否可以重写xpath表达式以更快地运行?可能有效的一件事是避免构建名称列表节点集(如果以后不需要它的话),并将节点计数在lxml中。像这样:

    start = datetime.now()
    name_list_len = root.xpath("count(/book/author/name/text())")
    print ("Size of list = " + str(name_list_len))
    end = datetime.now()
    

    否则,您可能会发现 expat parser 提取文本的速度更快,但它没有进行验证,而且使用起来更复杂(您需要编写一个状态机和几个回调)。如果只需要文本,使用 C implementation of the element tree API . 这个 lxml benchmarks 做有趣的阅读,并且确实似乎暗示你可以用它更快地提取文本。

    一个常见的XPath性能问题是表达式开头不需要使用“//”。在这种情况下,使表达式绝对化,例如:

     name_list = root.xpath("/rootelement/book/author/name/text()")
    

    如果文档的结构允许这样做,则速度会更快。不过,这不应该成为一个问题。

        2
  •  0
  •   Sumant    15 年前

    这个 lxml benchmarks 非常有用。在我看来,使用xpath提取元素节点很快,但提取文本可能很慢。下面,我有三个非常快的解决方案。

    def bench_lxml_xpath_direct(root): # Very slow but very fast if text() is removed.
      name_list = root.xpath("book/author/name/text()")
      print ("Size of list = " + str(len(name_list)))
    
    def bench_lxml_xpath_loop(root): # Fast
      name_list = root.xpath("book/author/name")
      result = []
      for n in name_list:
        result.append(n.text)
    
      print ("Size of list = " + str(len(name_list)))
    
    def bench_lxml_getiterator(tree): # Very fast
      result = []
      for name in tree.getiterator("name"):
        result.append(name.text)
      print ("Size of list = " + str(len(result)))
    
    
    def bench_lxml_findall(tree):  # Superfast
      result = []
      for name in tree.findall("//name"):
        result.append(name.text)
      print ("Size of list = " + str(len(result)))