python xml按属性值搜索

时间:2013-09-17 07:26:27

标签: python xml minidom

我正在使用python实用程序来搜索并在一个非常大的配置文件中显示记录的完整路径,存储为xml文件。 文件大小可以是12M,可以容纳294460行,并且可以增长到更大的大小。

这是一个例子(简化):

<?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>

我有兴趣在文件上运行并将所有保存搜索字符串的节点归档到节点的path或field属性中。因为节点“类型”(或节点名称)可以是或记录或字段,因此属性的名称可以从路径更改为名称。

我使用minidom来解析和搜索xml,但我的代码耗费了太多资源和太多时间。

这是我写的: xml_file_location是文件的位置 string_to_search是我在XML文件中搜索的字符串 我找到的那个节点的路径存储在type:record的节点中,名为: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 个答案:

答案 0 :(得分:2)

正如@Martijn Pieters所说,使用ElementTree,它是自Python 2.5以来的stdlib - 你正在使用“with”所以我假设你是2.6+。这很容易学习,它的心智模型非常接近DOM。

旧学校的替代方案是SAX解析,它永远存在于stdlib中的模块:基本上,只要解析器遇到打开或关闭标记,就会指定要执行的回调。它有点不自然(它强迫你考虑文本处理,而不是XML逻辑结构),但可以非常有效。

答案 1 :(得分: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