11

我一直在研究解析外部 XML 文件的代码。其中一些文件很大,高达千兆字节的数据。不用说,这些文件需要被解析为流,因为将它们加载到内存中效率太低,并且经常导致 OutOfMemory 麻烦。

我使用了 miniDOM、ElementTree、cElementTree 库,目前正在使用 lxml。现在我有一个工作的、非常节省内存的脚本,使用lxml.etree.iterparse. 问题是我需要解析的一些 XML 文件包含编码错误(它们宣传为 UTF-8,但包含不同编码的字符)。使用lxml.etree.parse时可以通过使用recover=True自定义解析器的选项来修复它,但iterparse不接受自定义解析器。(另见:这个问题

我当前的代码如下所示:

from lxml import etree
events = ("start", "end")
context = etree.iterparse(xmlfile, events=events)
event, root_element = context.next() # <items>
for action, element in context:
    if action == 'end' and element.tag == 'item':
    # <parse>
    root_element.clear() 

iterparse遇到错误字符时出错(在这种情况下,它是 a ^Y):

lxml.etree.XMLSyntaxError: Input is not proper UTF-8, indicate encoding !
Bytes: 0x19 0x73 0x20 0x65, line 949490, column 25

我什至不想解码这些数据,我可以放弃它。但是我不知道有什么方法可以跳过该元素 - 我尝试过context.nextcontinue在 try/except 语句中。

任何帮助,将不胜感激!

更新

一些附加信息:这是 iterparse 失败的行:

<description><![CDATA:[musea de la photographie fonds mercator. Met meer dan 80.000 foto^Ys en 3 miljoen negatieven is het Muse de la...]]></description>

根据 etree,错误发生在 bytes 0x19 0x73 0x20 0x65
根据 hexedit,19 73 20 65转换为 ASCII.s e
.这个地方应该是一个撇号(foto's)。

我还发现了这个问题,它没有提供解决方案。

4

4 回答 4

9

如果问题是实际的字符编码问题,而不是格式错误的 XML,最简单且可能最有效的解决方案是在文件读取点处理它。像这样:

import codecs
from lxml import etree
events = ("start", "end")
reader = codecs.EncodedFile(xmlfile, 'utf8', 'utf8', 'replace')
context = etree.iterparse(reader, events=events)

这将导致非 UTF8 可读字节被替换为“?”。还有其他一些选择;有关更多信息,请参阅编解码器模块的文档。

于 2012-07-09T18:01:52.417 回答
2

由于问题是由非法XML 字符引起的,在本例中是 0x19 字节,我决定将它们去掉。我在这个网站上找到了以下正则表达式:

invalid_xml = re.compile(u'[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]')

我编写了这段代码,在保存 xml 提要的同时删除非法字节:

conn = urllib2.urlopen(xmlfeed)
xmlfile = open('output', 'w')

while True:
    data = conn.read(4096)
    if data:
        newdata, count = invalid_xml.subn('', data)
        if count > 0 :
            print 'Removed %s illegal characters from XML feed' % count
        xmlfile.write(newdata)

    else:
        break

xmlfile.close()
于 2012-07-10T21:43:38.220 回答
1

我使用了一段类似的代码:

 illegalxml = re.compile(u'[\x00-\x08\x0b\x0c\x0e-\x1F\uD800-\uDFFF\uFFFE\uFFFF]')

...

illegalxml.sub("?",mystring)

...

但是,这不适用于所有可能的字符串(400+MB 字符串)。

对于最终解决方案,我使用如下解码/编码:

outxml = "C:/path_to/xml_output_file.xml"
with open(outxml, "w") as out:
    valid_xmlstring = mystring.encode('latin1','xmlcharrefreplace').decode('utf8','xmlcharrefreplace')
    out.write(valid_xmlstring) 
于 2013-09-01T00:37:06.123 回答
0

我的xml文件中的char“”也有类似的问题,这也是无效的xmlchar。这是因为在 xml 版本 1.0 中,不允许使用 �、 等字符。规则是不允许所有字符组合为正则表达式 '&#x[0-1]?[0-9A-E]'。我的目的是纠正一个巨大的 xml 文件中的无效字符,根据 Rik 的回答,我对其进行了如下改进:

import re

invalid_xml = re.compile(r'&#x[0-1]?[0-9a-eA-E];')

new_file = open('new_file.xml','w') 
with open('old_file.xml') as f:
    for line in f:
        nline, count = invalid_xml.subn('',line)
        new_file.write(nline) 
new_file.close()
于 2016-01-08T10:30:26.487 回答