Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Python中解析巨大、编码错误的XML文件_Python_Xml_Encoding_Iterparse - Fatal编程技术网

在Python中解析巨大、编码错误的XML文件

在Python中解析巨大、编码错误的XML文件,python,xml,encoding,iterparse,Python,Xml,Encoding,Iterparse,我一直在编写解析外部XML文件的代码。其中一些文件非常庞大,数据量高达千兆字节。不用说,这些文件需要作为流进行解析,因为将它们加载到内存中效率太低,并且常常会导致OutOfMemory问题 我已经使用了miniDOM、ElementTree、cElementTree库,目前我正在使用lxml。 现在,我有了一个非常有效的脚本,使用lxml.etree.iterparse。问题是我需要解析的一些XML文件包含编码错误(它们以UTF-8的形式发布,但包含不同编码的字符)。使用lxml.etree.p

我一直在编写解析外部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() 
我甚至不想解码这些数据,我可以把它放下。但是我不知道有什么方法可以跳过这个元素——我在try/except语句中尝试了
context.next
continue

任何帮助都将不胜感激

更新

其他一些信息: 这是iterparse失败的地方:

根据etree,错误发生在字节
0x19 0x73 0x20 0x65

根据hexedit,
19732065
转换为ASCII
.se

此处的
应该是撇号(foto's)


我还发现,这并没有提供解决方案。

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

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

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

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

我编写了这段代码,在保存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()

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

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

但是,这并不适用于所有可能的字符串(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) 

我的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()

您是否尝试过beautiful soup?执行预处理步骤以更正编码是否可行?您甚至可以在使用StringIO对象的管道中执行此操作,并将输出馈送到etree。@Danathean当然可以,关于如何执行此操作的任何提示?@Rik如果您组合一些代码来解析标记、属性和内容解析,您可以将有问题的输入馈送到chardet(请参阅),并在运行时重新编写文件。我不确定编码问题在文档中的何处,但如果它们是孤立的,则不会产生太多开销。请发布一个完整的XML文档,其中包括顶级标记和DTD(如果有)以及片段,以便其他人可以测试您正在测试的相同内容。此外,如果你能在错误前显示几个字节,这可能会有所帮助(这样我们就可以看到我们是否有半个UTF-8字符或其他)。嗯,这看起来是一个很好的解决方案,但我刚刚尝试过——在同一点上出现相同的错误,即使我将“替换”更改为“忽略”。(为了回答您上面的问题,这是Python2.7,不需要compat。)您可以将XML文件(或者,更好的,一个显示问题的小文档)发布到某个地方,以便人们可以帮助调试它吗?
illegalxml.sub("?",mystring)
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) 
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()