用Python解析庞大,编码错误的XML文件

Rik*_*Rik 11 python xml encoding iterparse

我一直在研究解析外部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() 
Run Code Online (Sandbox Code Playgroud)

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

lxml.etree.XMLSyntaxError: Input is not proper UTF-8, indicate encoding !
Bytes: 0x19 0x73 0x20 0x65, line 949490, column 25
Run Code Online (Sandbox Code Playgroud)

我甚至不想解码这些数据,我可以放弃它.不过,我不知道有什么办法跳过元素-我想context.nextcontinue中试/ 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,错误发生在字节0x19 0x73 0x20 0x65.
据hexedit的,19 73 20 65转换为ASCII .s e
.在这个地方应该是一个单引号(照片的).

我也发现了这个问题,但没有提供解决方案.

aba*_*ert 9

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

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

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


Rik*_*Rik 2

由于问题是由非法XML 字符(在本例中为 0x19 字节)引起的,因此我决定将其删除。我在这个网站上发现了以下正则表达式:

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

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

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()
Run Code Online (Sandbox Code Playgroud)