Python tree.iterparse 所选元素的导出源 XML 包括所有后代
Python tree.iterparse export source XML of selected element including all descendants
Python 3.4、使用etree.iterparse解析GB++大小XML维基百科转储文件。我想在当前匹配的 <page>
元素中测试它的 <ns>
值,根据后一个值我想导出整个 <page>
对象的源 XML 和所有它的内容包括嵌套在其中的任何元素,即整篇文章的 XML。
我可以迭代 <page>
对象并找到我想要的对象,但是所有可用的函数似乎都想读取 text/attribute 值,而我只想要源的 utf8 字符串副本范围 <page>
对象的完整文件 XML 代码。这可能吗?
XML 的精简版如下所示:
<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xml:lang="en">
<page>
<title>Some Article</title>
<ns>0</ns>
<revision>
<timestamp>2017-07-27T00:59:41Z</timestamp>
<text xml:space="preserve">some text</text>
</revision>
</page>
<page>
<title>User:Wonychifans</title>
<ns>2</ns>
<revision>
<text xml:space="preserve">blah blah</text>
</revision>
</page>
</mediawiki>
让我进行 <ns>
值测试的 python 代码在这里:
``from lxml import etree
# store namespace string for all elements (only one used in Wikipedia XML docs)
NAMESPACE = '{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',))
for event, elem in context:
# at end of parsing each
if elem.tag == (NAMESPACE+'page') and event == 'end':
tagNs = elem.find('wiki:ns',ns)
if tagNs is not None:
nsValue = tagNs.text
if nsValue == '2':
# export the current <page>'s XML code
在这种情况下,我只想提取 第二个 <page>
元素的 XML 代码,即一个字符串:
<page>
<title>User:Wonychifans</title>
<ns>2</ns>
<revision>
<text xml:space="preserve">blah blah</text>
</revision>
</page>
编辑:小错字和更好的标记
你可以做到。
>>> 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。
首先,寻找 page
元素的起始行。
>>> 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 以使用起点找到完整的 page
条目。
>>> 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>
我已将 Bill Bell 的回答标记为已接受,因为它有助于我获得最终解决方案,其核心如下。外层循环让我循环遍历 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()
我相信这可以更优雅,但我希望这能帮助任何非专家来到这里并尝试做这种事情。
Python 3.4、使用etree.iterparse解析GB++大小XML维基百科转储文件。我想在当前匹配的 <page>
元素中测试它的 <ns>
值,根据后一个值我想导出整个 <page>
对象的源 XML 和所有它的内容包括嵌套在其中的任何元素,即整篇文章的 XML。
我可以迭代 <page>
对象并找到我想要的对象,但是所有可用的函数似乎都想读取 text/attribute 值,而我只想要源的 utf8 字符串副本范围 <page>
对象的完整文件 XML 代码。这可能吗?
XML 的精简版如下所示:
<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xml:lang="en">
<page>
<title>Some Article</title>
<ns>0</ns>
<revision>
<timestamp>2017-07-27T00:59:41Z</timestamp>
<text xml:space="preserve">some text</text>
</revision>
</page>
<page>
<title>User:Wonychifans</title>
<ns>2</ns>
<revision>
<text xml:space="preserve">blah blah</text>
</revision>
</page>
</mediawiki>
让我进行 <ns>
值测试的 python 代码在这里:
``from lxml import etree
# store namespace string for all elements (only one used in Wikipedia XML docs)
NAMESPACE = '{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',))
for event, elem in context:
# at end of parsing each
if elem.tag == (NAMESPACE+'page') and event == 'end':
tagNs = elem.find('wiki:ns',ns)
if tagNs is not None:
nsValue = tagNs.text
if nsValue == '2':
# export the current <page>'s XML code
在这种情况下,我只想提取 第二个 <page>
元素的 XML 代码,即一个字符串:
<page>
<title>User:Wonychifans</title>
<ns>2</ns>
<revision>
<text xml:space="preserve">blah blah</text>
</revision>
</page>
编辑:小错字和更好的标记
你可以做到。
>>> 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。
首先,寻找 page
元素的起始行。
>>> 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 以使用起点找到完整的 page
条目。
>>> 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>
我已将 Bill Bell 的回答标记为已接受,因为它有助于我获得最终解决方案,其核心如下。外层循环让我循环遍历 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()
我相信这可以更优雅,但我希望这能帮助任何非专家来到这里并尝试做这种事情。