簡體   English   中英

使用 lxml 和 iterparse() 來解析一個大 (+- 1Gb) XML 文件

[英]using lxml and iterparse() to parse a big (+- 1Gb) XML file

我必須解析具有如下結構的 1Gb XML 文件,並提取標簽“作者”和“內容”中的文本:

<Database>
    <BlogPost>
        <Date>MM/DD/YY</Date>
        <Author>Last Name, Name</Author>
        <Content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.</Content>
    </BlogPost>

    <BlogPost>
        <Date>MM/DD/YY</Date>
        <Author>Last Name, Name</Author>
        <Content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.</Content>
    </BlogPost>

    [...]

    <BlogPost>
        <Date>MM/DD/YY</Date>
        <Author>Last Name, Name</Author>
        <Content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.</Content>
    </BlogPost>
</Database>

到目前為止,我已經嘗試了兩件事:i) 讀取整個文件並使用 .find(xmltag) 和 ii) 使用 lxml 和 iterparse() 解析 xml 文件。 我已經讓它工作的第一個選項,但它很慢。 第二個選擇我還沒有設法讓它起步。

這是我所擁有的一部分:

for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
    if element.tag == "BlogPost":
        print element.text
    else:
        print 'Finished'

結果只是空格,其中沒有文本。

我一定是做錯了什么,但我無法理解。 另外,如果它不夠明顯,我對 python 很陌生,這是我第一次使用 lxml。 請幫忙!

for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
  for child in element:
    print(child.tag, child.text)
    element.clear()

最后清除將阻止您使用太多內存。

[更新:] 將“......之間的一切都作為一個字符串”我想你想要一個:

for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
  print(etree.tostring(element))
  element.clear()

或者

for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
  print(''.join([etree.tostring(child) for child in element]))
  element.clear()

或者甚至:

for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
  print(''.join([child.text for child in element]))
  element.clear()

對於未來的搜索者:這里的最佳答案建議在每次迭代時清除元素,但這仍然會給您留下不斷增加的空元素集,這些空元素將在內存中慢慢積累:

for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
  for child in element:
    print(child.tag, child.text)
    element.clear()

^ 這不是一個可擴展的解決方案,尤其是當您的源文件越來越大時。 更好的解決方案是獲取元素,並每次加載完整記錄時清除 這將保持內存使用相當穩定(我會說低於 20MB)。

這是一個不需要查找特定標簽的解決方案。 此函數將返回一個生成器,該生成器生成根節點(例如<Database> )下的所有第一個子節點(例如<BlogPost>元素)。 它通過記錄根節點之后第一個標簽的開始,然后等待相應的結束標簽,產生整個元素,然后清除根節點來實現這一點。

from lxml import etree

xmlfile = '/path/to/xml/file.xml'

def iterate_xml(xmlfile):
    doc = etree.iterparse(xmlfile, events=('start', 'end'))
    _, root = next(doc)
    start_tag = None
    for event, element in doc:
        if event == 'start' and start_tag is None:
            start_tag = element.tag
        if event == 'end' and element.tag == start_tag:
            yield element
            start_tag = None
            root.clear()

對於這樣的事情,我更喜歡XPath

In [1]: from lxml.etree import parse

In [2]: tree = parse('/tmp/database.xml')

In [3]: for post in tree.xpath('/Database/BlogPost'):
   ...:     print 'Author:', post.xpath('Author')[0].text
   ...:     print 'Content:', post.xpath('Content')[0].text
   ...: 
Author: Last Name, Name
Content: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.
Author: Last Name, Name
Content: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.
Author: Last Name, Name
Content: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.

不過,我不確定它在處理大文件方面是否有所不同。 對此的評論將不勝感激。

按照你的方式去做,

for event, element in etree.iterparse(path_to_file, tag="BlogPost"):
     for info in element.iter():
         if info.tag in ('Author', 'Content'):
             print info.tag, ':', info.text

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM