Быстрый Python XML валидатор с поддержкой XPath - PullRequest
4 голосов
/ 20 февраля 2010

Мне нужно прочитать большой XML (65 Мб), проверить его по 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 ]

0 голосов
/ 21 февраля 2010

Тесты lxml весьма полезны. Мне кажется, что извлечение узлов элементов с использованием 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)))
0 голосов
/ 20 февраля 2010

Интересно, можно ли переписать выражение xpath, чтобы оно работало быстрее? Единственное, что может сработать, - это избежать построения набора узлов name_list (если он вам не понадобится позже) и подсчета узлов внутри 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()

В противном случае вы можете найти синтаксический анализатор быстрее для извлечения текста, но он не проверяется и более сложен в использовании (вам нужно написать конечный автомат и пару обратные вызовы). Если вам просто нужен текст, может быть быстрее использовать реализацию C дерева элементов API . lxml отметки делают интересное чтение и, похоже, намекают на то, что с этим вы сможете быстрее извлечь текст.

Одной из распространенных проблем производительности xpath является ненужное использование «//» в начале выражения. В этом случае, сделав выражение абсолютным, например ::

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

может быть намного быстрее, если документ структурирован, чтобы разрешить это. Здесь не должно быть проблем.

...