我一直致力于解析外部XML文件的代码。其中一些文件非常庞大,高达千兆字节的数据。毋庸置疑,这些文件需要被解析为流,因为将它们加载到内存中的效率太低,并且常常导致OutOfMemory问题。
我使用了库miniDOM,ElementTree,cElementTree,我目前正在使用lxml。
现在我使用lxml.etree.iterparse
有一个工作的,非常有效的内存脚本。问题是我需要解析的一些XML文件包含编码错误(它们通告为UTF-8,但包含不同编码的字符)。使用lxml.etree.parse
时,可以使用自定义解析器的recover=True
选项修复此问题,但iterparse
不接受自定义解析器。 (另见:this question)
我目前的代码如下:
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
遇到错误字符时出错(在这种情况下,它是^Y
):
lxml.etree.XMLSyntaxError: Input is not proper UTF-8, indicate encoding !
Bytes: 0x19 0x73 0x20 0x65, line 949490, column 25
我甚至不想解码这些数据,我可以放弃它。但是我不知道跳过该元素的任何方法 - 我在try / except语句中尝试了context.next
和continue
。
任何帮助将不胜感激!
更新
其他一些信息: 这是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
这个地方的.
应该是撇号(foto's)。
我还发现了this question,它没有提供解决方案。
答案 0 :(得分: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可读字节被&#39;?&#39;替换。还有其他一些选择;有关更多信息,请参阅编解码器模块的文档。
答案 1 :(得分:2)
由于问题是由非法 XML字符引起的,在这种情况下是0x19字节,我决定将它们剥离。我找到了以下正则表达式on this site:
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()
答案 2 :(得分: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)
答案 3 :(得分:0)
我和char&#34;&#34;有类似的问题。在我的xml文件中,它也是无效的xmlchar。这是因为在xml版本1.0中,不允许使用像&amp;#x0,&amp; #xE这样的字符。规则是所有角色组成为正则表达式&#39;&amp; #x [0-1]?[0-9A-E]&#39;不允许。我的目的是纠正一个巨大的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()