2013-09-17 3 views
0

Я работаю над утилитой python для поиска и представления полного пути к записи в очень большом файле конфигурации, хранящемся в виде XML-файла. Размер файла может быть 12M и может содержать 294460 строк и может вырасти до гораздо большего размера.python xml поиск по значению атрибута

Вот пример (упрощенно):

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> 
<root version="1.1.1"> 
    <record path=""> 
    <record path="path1"> 
     <field name="some_name1" value="1234"/> 
     <record path="path2"> 
     <field name="0" value="abcd0"/> 
     <field name="1" value="abcd1"/> 
     <field name="2" value="abcd2"/> 
     <field name="28" value="abcd28"/> 
     <field name="29" value="abcd29"/> 
     </record> 
    </record> 
    <record path="pathx"> 
     <record path="pathy"> 
     <record path="pathz"> 
     </record> 
     <record path="pathv"> 
      <record path="pathw"> 
      <field name="some_name1" value="yes"/> 
      <field name="some_name2" value="2084"/> 
      <field name="some_buffer_name" value="14"/> 
      <record path="cache_value"> 
       <field name="some_name7000" value="12"/> 
      </record> 
    </record> 
     <record path="path_something"> 
      <field name="key_word1" value="8"/> 
      <field name="key_word2" value="9"/> 
      <field name="key_word3" value="10"/> 
      <field name="key5" value="1"/> 
      <field name="key6" value="1"/> 
      <field name="key7" value="yes"/> 
    </record> 
    </record> 
</root> 

Я заинтересован, чтобы запустить файл и файл все узлы, которые держат строку поиска в пути или поля атрибута узла. Потому что «тип» узла (или имя узла) может быть или записывать или поле, и, таким образом, имя атрибута может меняться от пути к имени.

Я использовал minidom для разбора и поиска в xml, но мой код берет слишком много ресурсов и слишком много времени.

Это то, что я писал: xml_file_location является расположение файла string_to_search является строка, я искать в файле XML и путь к этому узлу, который я нашел хранится в узле типа: запись , в атрибуте named: path, и это то, что я печатаю пользователю.

with open(xml_file_location, 'r') as inF: 
# search the xml file for lines with the string for search 
    for line in inF: 
     if string_to_search in line: 
      found_counter = found_counter + 1 
      node_type = line.strip(" ").split(" ")[0] 
      node_type = re.sub('[^A-Za-z0-9]+', '', node_type) 
      node_attr = line.strip(" ").split(" ")[1] 
      node_value = re.sub('[^A-Za-z0-9_]+', '', node_attr.split("=")[1]) 
      node_attr = re.sub('[^A-Za-z0-9]+', '', node_attr.split("=")[0]) 
      #print node_type 
      #print node_attr 
      #print node_value 
      if node_type in lines_dict: 
       if not node_attr in lines_dict[node_type]: 
        lines_dict[node_type][node_attr] = [nome_value] 
       elif not node_value in lines_dict[node_type][node_attr]: 
         lines_dict[node_type][node_attr].append(node_value) 
      else: 
       lines_dict[node_type] = {} 
       lines_dict[node_type][node_attr] = [node_value] 

print "Found: %s strings in the xml file" %found_counter 

#pp = pprint.PrettyPrinter(indent=4) 
#pp.pprint(lines_dict) 

print "Parsing the xml file" 
dom = parse(xml_file_location) 

print "Locating the full path" 

for node_type in lines_dict: 
# for all types of node 
    elements = dom.getElementsByTagName(node_type) 
    # create the elements for those nodes 
    for node in elements: 
    # go over all nodes in the elements 
     if node.hasAttribute(node_attr): 
      if node.getAttribute(node_attr) in lines_dict[node_type][node_attr]: 
      # check if the attribute appears in the lines dict 
       result = node.getAttribute(node_attr) # holds the path 
       parent = node.parentNode  # create a pointer to point on the parent node 
       while parent.getAttribute("path") != "": 
       # while didn't reach the root of the conf - a record that has an empty path attribute 
        result = parent.getAttribute("path") + "." + result  # add the path of the parent to the full path 
        parent = parent.parentNode        # advance the parent pointer 
       print 
       print "Found: %s" %node.toprettyxml().split("\n")[0] 
       print "Path: %s" %result 

, например: я буду искать: abcd1 утилита напечатает полный путь: path1.path2 или я буду искать: pathw и утилита будет возвращать: pathx.pathy.pathv

Я понимаю, что это очень неэффективно, я просматриваю все узлы в конфиге и сравниваю их с тем, что я помещал в list_dic в простой поиск строки.

Я пытался использовать внешние модули, чтобы сделать это, но без успеха

Я ищу более эффективный способ сделать этот вид поиска, и я очень ценю помощь.

+2

Вы * действительно * не хотите использовать minidom для этого; вместо этого используйте API ElementTree, который поддерживает итеративный синтаксический анализ. Извлеките только то, что вам нужно, когда будете разбираться. –

+0

Читайте здесь: http: // effbot.орг/зона/элемент-iterparse.htm; ElementTree является частью стандартной библиотеки Python: http://docs.python.org/2/library/xml.etree.elementtree.html –

+0

Спасибо Martijn, Моя первая попытка заключалась в использовании xml.etree.ElemntTree, , но Я запутался в этом, так как я ищу значение атрибута, и я не знаю, каким будет тип атрибута - будет ли это атрибут с именем «путь» или «имя», и, кроме того, я не знаю, каков уровень узла - это может быть дочерний корень или дочерний элемент дочернего элемента дочернего элемента root. Поэтому я попытался изменить Xpath: http://docs.python.org/2/library/xml.etree.elementtree.html#xpath-support , но не смог изменить выражение xpath Element.findall(). – Elia

ответ

0

Это решение, которое я получил с помощью XPath и LXML:

root = LXML.parse(xml_file_location) elements_list = root.xpath(".//*[@*[contains(., $text)]]", text = string_to_search)

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

используя LXML getpaerent() метод я спрашиваю каждый узел «который является вашим родителем»

это код:

root = LXML.parse(xml_file_location) 
elements_list = root.xpath(".//*[@*[contains(., $text)]]", text = string_to_search) 
print "Found: %s strings in the xml file" %len(elements_list) 
for node in elements_list: 
    print "\nFound:\n%s" %LXML.tostring(node).split("\n")[0] 
    parent = node.getparent() 
    if node.tag == "record": 
     path = node.get("path") # nodes of type: "record" hold the attribute: "path" 
    elif node.tag == "field": 
     path = node.get("name") # nodes of type: "field" hold the attribute: "name" 
    else: 
     print "unclear node type adding empty string" 
     path = "" 
    full_path = path 
    while parent.get("path") != "": 
     parent_path = parent.get("path") 
     full_path = parent_path + "." + full_path 
     parent = parent.getparent() 
    print "Full path: %s" %full_path 
2

Как сказал @Martijn Pieters, используйте ElementTree, который находится в stdlib с Python 2.5 - вы используете «с», поэтому я предполагаю, что вы на 2.6+. Его достаточно легко изучить, и его ментальная модель очень близка к DOM.

Альтернатива старой школе - это синтаксический анализ SAX, который навсегда имеет модуль в stdlib: в основном вы указываете обратные вызовы для выполнения всякий раз, когда парсер сталкивается с открытием или закрытием тегов. Это немного неестественно (это заставляет вас думать в терминах обработки текста, а не XML логических структур), но может быть очень эффективным.

Смежные вопросы