[英]Parsing XML: find element sub-tree without a loop
我正在使用ElementTree解析 XML 負載。 我無法共享確切的代碼或文件,因為它共享敏感信息。 我能夠通過迭代一個元素(如 ElementTree 文檔中所見)並將輸出附加到列表來成功提取我需要的信息。 例如:
list_col_name = []
list_col_value = []
for col in root.iter('my_table'):
# get col name
col_name = col.find('col_name').text
list_col_name.append(col_name
# get col value
col_value = col.find('col_value').text
list_col_value.append(col_value)
我現在可以將這些放入字典中,然后繼續剩下的工作:
dict_ = dict(zip(list_col_name, list_col_value))
但是,我需要盡快發生這種情況,並且想知道是否有一種方法可以立即提取list_col_name
(即,使用findall()
或類似方法)。 如果可能的話,只是想知道如何提高 xml 解析的速度。 感謝所有答案/建議。 先感謝您。
我的建議是使用基於iterparse方法的源文件的“增量”解析。 原因是你實際上:
另一個提示是使用lxml庫,而不是ElementTree 。 原因是雖然這兩個庫中都存在iterparse方法,但lxml版本有額外的tag參數,所以你可以“限制”循環只處理感興趣的標簽。
作為我使用的源文件(類似):
<root>
<my_table id="t1">
<col_name>N1</col_name>
<col_value>V1</col_value>
<some_other_stuff>xx1</some_other_stuff>
</my_table>
<my_table id="t2">
<col_name>N2</col_name>
<col_value>V2</col_value>
<some_other_stuff>xx1</some_other_stuff>
</my_table>
<my_table id="t3">
<col_name>N3</col_name>
<col_value>V3</col_value>
<some_other_stuff>xx1</some_other_stuff>
</my_table>
</root>
實際上,我的源文件:
my_table
元素(不是3 個),some_other_stuff
重復 8 次(在每個my_table
),以模擬每個my_table
包含的其他元素。我使用%timeit進行了 3 次測試:
您的循環,預先解析源 XML 文件:
from lxml import etree as et def fn1(): root = et.parse('Tables.xml') list_col_name = [] list_col_value = [] for col in root.iter('my_table'): col_name = col.find('col_name').text list_col_name.append(col_name) col_value = col.find('col_value').text list_col_value.append(col_value) return dict(zip(list_col_name, list_col_value))
執行時間為 1.74 毫秒。
我的循環,基於iterparse ,只處理“必需”元素:
def fn2(): key = '' dict_ = {} context = et.iterparse('Tables.xml', tag=['my_table', 'col_name', 'col_value']) for action, elem in context: tag = elem.tag txt = elem.text if tag == 'col_name': key = txt elif tag == 'col_value': dict_[key] = txt elif tag == 'my_table': elem.clear() elem.getparent().remove(elem) return dict_
我假設在每個my_table元素中col_name出現在col_value之前,並且每個my_table只包含一個名為col_name和col_value 的子元素。
另請注意,上述函數清除每個my_table元素並將其從解析的 XML 樹中刪除( getparent函數僅在lxml版本中可用)。
另一個改進是我“直接”將每個鍵/值對添加到此函數返回的字典中,因此不需要zip 。
執行時間為 1.33 毫秒。 不是很快,但至少可以看到一些時間增益。
您還可以讀取所有col_name和col_value元素,調用findall然后調用zip :
def fn3(): root = et.parse('Tables.xml') list_col_name = [] for elem in root.findall('.//col_name'): list_col_name.append(elem.text) list_col_value = [] for elem in root.findall('.//col_value'): list_col_value.append(elem.text) return dict(zip(list_col_name, list_col_value))
執行時間為 1.38 毫秒。 也比您的原始解決方案更快,但與我的第一個解決方案( fn2 )沒有顯着差異。
當然,最終結果很大程度上取決於:
考慮使用findall
進行列表理解以避免列表初始化/追加和顯式for
循環,這可能會略微提高性能:
# FINDALL LIST COMPREHENSION
list_col_name = [e.text for e in root.findall('./my_table/col_name')]
list_col_value = [e.text for e in root.findall('./my_table/col_value')]
dict(zip(list_col_name, list_col_value))
或者,使用完全支持 XPath 1.0 的lxml
(第三方庫),考慮xpath()
可以將解析輸出直接分配給列表,同時避免初始化/追加和for
循環:
import lxml.etree as et
...
# XPATH LISTS
list_col_name = root.xpath('my_table/col_name/text()')
list_col_value = root.xpath('my_table/col_value/text()')
dict(zip(list_col_name, list_col_value))
不知道有沒有你想要的
from simplified_scrapy import SimplifiedDoc
html = '''
<?xml version="1.0"?>
<data>
<country name="Liechtenstein">
<rank>1</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank>4</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama">
<rank>68</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>
'''
doc = SimplifiedDoc(html)
ranks = doc.selects('country>(rank>text())')
print (ranks)
ranks = doc.selects('country>rank()')
print (ranks)
ranks = doc.selects('country>children()')
print (ranks)
結果:
['1', '4', '68']
[{'tag': 'rank', 'html': '1'}, {'tag': 'rank', 'html': '4'}, {'tag': 'rank', 'html': '68'}]
[[{'tag': 'rank', 'html': '1'}, {'tag': 'year', 'html': '2008'}, {'tag': 'gdppc', 'html': '141100'}, {'name': 'Austria', 'direction': 'E', 'tag': 'neighbor'}, {'name': 'Switzerland', 'direction': 'W', 'tag': 'neighbor'}], [{'tag': 'rank', 'html': '4'}, {'tag': 'year', 'html': '2011'}, {'tag': 'gdppc', 'html': '59900'}, {'name': 'Malaysia', 'direction': 'N', 'tag': 'neighbor'}], [{'tag': 'rank', 'html': '68'}, {'tag': 'year', 'html': '2011'}, {'tag': 'gdppc', 'html': '13600'}, {'name': 'Costa Rica', 'direction': 'W', 'tag': 'neighbor'}, {'name': 'Colombia', 'direction': 'E', 'tag': 'neighbor'}]]
這里有更多例子: https : //github.com/yiyedata/simplified-scrapy-demo/tree/master/doc_examples
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.