Python tree.iterparse导出所选元素的源XML,包括所有子体

Python tree.iterparse导出所选元素的源XML,包括所有子体,python,elementtree,iterparse,Python,Elementtree,Iterparse,Python 3.4,使用etree.iterparse解析GB++大小的XML Wikipedia转储文件。我想在当前匹配的元素中测试其值,然后根据后一个值导出整个对象的源XML及其所有内容,包括嵌套在其中的任何元素,即整篇文章的XML 我可以迭代对象并找到我想要的对象,但是所有可用的函数似乎都想要读取文本/属性值,而我只是想要一个源文件XML代码的utf8字符串副本,用于完整的范围对象。这可能吗 XML的精简版本如下所示: 一些文章 0 2017-07-27T00:59:41Z 一些文本

Python 3.4,使用etree.iterparse解析GB++大小的XML Wikipedia转储文件。我想在当前匹配的
元素中测试其
值,然后根据后一个值导出整个
对象的源XML及其所有内容,包括嵌套在其中的任何元素,即整篇文章的XML

我可以迭代
对象并找到我想要的对象,但是所有可用的函数似乎都想要读取文本/属性值,而我只是想要一个源文件XML代码的utf8字符串副本,用于完整的范围
对象。这可能吗

XML的精简版本如下所示:


一些文章
0
2017-07-27T00:59:41Z
一些文本
用户:Wonychifans
2.
废话
让我进行
值测试的python代码如下:

从lxml导入etree #存储所有元素的名称空间字符串(仅在Wikipedia XML文档中使用一个) 名称空间={http://www.mediawiki.org/xml/export-0.10/}' ns={'wiki':'http://www.mediawiki.org/xml/export-0.10/'} context=etree.iterparse('src.xml',events=('end',)) 对于事件,上下文中的元素: #在解析每个 如果elem.tag==(名称空间+‘页面’)和event==‘结束’: tagNs=elem.find('wiki:ns',ns) 如果标记不是无: nsValue=tagNs.text 如果nsValue==“2”: #导出当前的XML代码 在本例中,我只想提取第二个
元素的XML代码,即包含以下内容的字符串:


用户:Wonychifans
2.
废话
编辑:较小的打字错误和更好的标记

您可以这样做

>>> from lxml import etree
>>> mediawiki = etree.iterparse('mediawiki.xml')
>>> page_content = {}
>>> for ev, el in mediawiki:
...     if el.tag=='page':
...         if page_content['ns']=='2':
...             print (page_content)
...         page_content = {}
...     else:
...         page_content[el.tag.replace('{http://www.mediawiki.org/xml/export-0.10/}', '')] = \
...             el.text.strip() if el.text else None
... 
>>> page_content
{'mediawiki': '', 'revision': '', 'timestamp': '2017-07-27T00:59:41Z', 'title': 'User:Wonychifans', 'page': '', 'text': 'blah blah', 'ns': '2'}
因为输出xml的结构非常简单,所以从字典构造它应该不会有困难

编辑:尽管这种方法需要两次通过xml文件,但速度可能更快,而且确实可以恢复所需的xml

首先,查找
页面
元素的起始行

>>> from lxml import etree
>>> mediawiki = etree.iterparse('mediawiki.xml', events=("start", "end"))
>>> for ev, el in mediawiki:
...     tag = el.tag[1+el.tag.rfind('}'):]
...     if ev=='start' and tag=='page':
...         keep=False
...     if ev=='start' and tag=='ns' and el.text=='2':
...         keep=True
...     if ev=='end' and tag=='page' and keep:
...         print (el.sourceline)
... 
10
再次浏览xml,使用起始点查找完整的
页面
条目

>>> with open('mediawiki.xml') as mediawiki:
...     for _ in range(9):
...         r = next(mediawiki)
...     for line in mediawiki:
...         print (line.strip())
...         if '</page>' in line:
...             break
...         
<page>
<title>User:Wonychifans</title>
<ns>2</ns>
<revision>
<text xml:space="preserve">blah blah</text>
</revision>
</page>
>将open('mediawiki.xml')作为mediawiki:
...     对于范围(9)内的uu:
...         r=下一个(mediawiki)
...     对于mediawiki中的行:
...         打印(line.strip())
...         如果“”在第行中:
...             打破
...         
用户:Wonychifans
2.
废话

我已将比尔·贝尔的答案标记为已被接受,因为它有助于我找到最终解决方案,其核心内容如下。外部循环允许我循环50多个源XML文件

由于某些源很大,代码会在循环中检查复制的源数据是否超过1GB。如果是这样,则会将数据写入文件,并清除缓冲区字符串变量。否则,所有提取的数据将在读取源文件结束时写入

进一步的改进是监视输出文件的大小,并在超过给定大小时切换输出源。在这种情况下,每次运行脚本时只扫描整个源代码集的一部分更容易

为了简洁起见,我删除了一些日志记录和打印语句:

<!-- language: lang-python -->

import sys

dataSourceStr = '/Users/x/WP-data/'
outputDataStr = '/Users/x/WP-data/ns-data/'
headfile = open("header.txt","r")
headStr = headfile.read()
headfile.close()
footStr = '</mediawiki>'
matchCount = 0
strPage = ''
strPage = headStr
fileNum = 20 
nameSpaceValue = 4
startNum = 41 # starting file number
lastNum = 53 # ending file number
endNum = lastNum + 1
outputDataFile = outputDataStr + 'ns' + str(nameSpaceValue) + '.xml'

for fileNum in range (startNum , endNum):
  with open(dataSourceStr + str(fileNum) + '.xml') as mediawiki:
    lineNum = 44
    blnKeep = False
    strPage = ''
    strItem = ''
    loopMatchCount = 0
    for _ in range(lineNum):
      r = next(mediawiki)
    for line in mediawiki:
      if '<ns>' + str(nameSpaceValue) + '</ns>' in line:
        blnKeep = True
        matchCount = matchCount + 1
        loopMatchCount = loopMatchCount + 1
      strItem = strItem + line
      lineNum = lineNum + 1
      if '</page>' in line:
        if blnKeep:
          strPage = strPage + strItem
          strItem = ''
          blnKeep = False
          strPageSize = sys.getsizeof(strPage)
          if strPageSize > 1073741824:
            file = open(outputDataFile,"a")
            file.write(strPage)
            file.close()
            strPage = ''
        else:
          strItem = ''

  mediawiki.close
  file = open(outputDataFile,"a")
  file.write(strPage)
  file.close()

file = open(outputDataFile,"a")
file.write(footStr)
file.close()

导入系统
dataSourceStr='/Users/x/WP data/'
outputDataStr='/Users/x/WP data/ns data/'
headfile=open(“header.txt”、“r”)
headStr=headfile.read()
headfile.close()文件
footStr=''
匹配计数=0
strPage=''
strPage=headStr
fileNum=20
nameSpaceValue=4
startNum=41#起始文件号
lastNum=53#结束文件编号
endNum=lastNum+1
outputDataFile=outputDataStr+'ns'+str(nameSpaceValue)+'.xml'
对于范围内的fileNum(startNum、endNum):
将open(dataSourceStr+str(fileNum)+'.xml')作为mediawiki:
lineNum=44
blnKeep=False
strPage=''
横纹=“”
loopMatchCount=0
对于范围内的u(lineNum):
r=下一个(mediawiki)
对于mediawiki中的行:
如果行中有“+str(nameSpaceValue)+”:
blnKeep=True
匹配计数=匹配计数+1
loopMatchCount=loopMatchCount+1
横纹=横纹+线条
lineNum=lineNum+1
如果“”在第行中:
如果blnKeep:
strPage=strPage+strItem
横纹=“”
blnKeep=False
strPageSize=sys.getsizeof(strPage)
如果strPageSize>1073741824:
文件=打开(outputDataFile,“a”)
file.write(strPage)
file.close()文件
strPage=''
其他:
横纹=“”
mediawiki.close
文件=打开(outputDataFile,“a”)
file.write(strPage)
file.close()文件
文件=打开(outputDataFile,“a”)
file.write(footStr)
file.close()文件

我相信这可能会更优雅,但我希望这能帮助任何来到这里并尝试做这类事情的非专家同事。

谢谢,但这没有多大帮助,因为我简化了XML示例,重新构建XML将使整个过程比想象的慢/复杂(而且我不知道如何做)。谢谢。当然,第二位代码需要加载整个XML文件,在本例中为65GB。这就是我使用iterparse的原因(更准确地说,我已经成功地将它用于8GB RAM MacBookAir上此类c.10GB文件的其他任务)。另一个问题是,我认为,代码假设所有
的长度都相同,因此我认为我需要迭代第一次传递的
el.sourceline
值列表。不,第二位代码逐行读取xml文件。它也不假设页面的长度相同。请注意,它跳过xml文件的前九行,然后输出行,直到找到包含“”的行,然后停止输出行。可能令人困惑的是,您将需要编写代码来保持读取的行数。在循环中,它将跳过,直到到达一个起点,然后输出,直到结束