繁体   English   中英

在解析大型XML文件时,我应该如何处理Python的lxml中的XMLSyntaxError?

[英]How should I deal with an XMLSyntaxError in Python's lxml while parsing a large XML file?

我正在尝试用Python的lxml库解析一个超过2GB的XML文件。 不幸的是,XML文件没有告诉字符编码的行,所以我必须手动设置它。 虽然迭代文件,但仍有一些奇怪的字符偶尔会出现。

我不确定如何确定行的字符编码,但此外,lxml将从for循环的范围引发XMLSyntaxError。 如何正确捕获此错误并正确处理? 这是一个简单的代码片段:

from lxml import etree
etparse = etree.iterparse(file("my_file.xml", 'r'), events=("start",), encoding="CP1252")
for event, elem in etparse:
    if elem.tag == "product":
        print "Found the product!"
        elem.clear()

这最终会产生错误:

XMLSyntaxError: PCDATA invalid Char value 31, line 1565367, column 50

该文件的那一行看起来像这样:

% sed -n "1565367 p" my_file.xml
<romance_copy>Ravioli Florentine. Tender Ravioli Filled With Creamy Ricotta Cheese And

填充的'F'实际上在我的终端中看起来像这样:

导致错误的xml行

在这里做的正确的事情是确保XML文件的创建者确保:A。)文件的编码被声明为B.)XML文件格式正确(没有无效的字符控制字符,没有无效不属于编码方案的字符,所有元素都已正确关闭等。)C。)如果要确保某些属性/元素存在,具有某些值或对应于某种格式,请使用DTD或XML模式(注意:这会受到性能影响)

所以,现在问你的问题。 当您使用它来解析XML时,LXml支持一大堆参数。 查看文档 您将需要查看这两个参数:

- > recover - >尽量解析破碎的XML
- > huge_tree - >禁用安全限制并支持非常深的树和非常长的文本内容(仅影响libxml2 2.7+)

它们会在某种程度上帮助你,但是某些无效字符可能无法恢复,因此再次确保正确编写文件是清理/正常工作代码的最佳选择。

啊是的还有一件事。 2GB是巨大的。 我假设你在这个文件中有一个类似元素的列表(示例书籍列表)。 尝试在操作系统上使用Regex Expression拆分文件,然后启动多个进程以分割碎片。 这样,您就可以在盒子上使用更多的核心,处理时间也会缩短。 当然,您必须处理将结果合并在一起的复杂性。 我不能为你做这个交易,但想把它作为“思考的食物”给你

除了帖子:如果您无法控制输入文件并且其中包含错误字符,我会尝试通过迭代字符串来替换/删除这些错误字符,然后再将其解析为文件。 这是一个删除您不需要的Unicode控制字符的代码示例:

#all unicode characters from 0x0000 - 0x0020 (33 total) are bad and will be replaced by "" (empty string)
for line in fileinput.input(xmlInputFileLocation, inplace=1):
    for pos in range(0,len(line)):
        if unichr(line[pos]) < 32:
            line[pos] = None
    print u''.join([c for c in line if c])

我也遇到了这个问题,得到数据中的\\x16 (unicode'同步空闲'或'SYN'字符,在xml中显示为^V ),这在解析xml时会导致错误: XMLSyntaxError: PCDATA invalid Char value 22. 22是因为ord('\\x16')是22。

@michael的回答让我走上正轨。 但是一些低于32的控制字符很好,比如返回或制表符,而一些较高的字符仍然很糟糕。 所以:

# Get list of bad characters that would lead to XMLSyntaxError.
# Calculated manually like this:
from lxml import etree
from StringIO import StringIO
BAD = []
for i in range(0, 10000):
    try:
        x = etree.parse(StringIO('<p>%s</p>' % unichr(i)))
    except etree.XMLSyntaxError:
        BAD.append(i)

这导致一个31个字符的列表,可以硬编码而不是在代码中进行上述计算:

BAD = [
    0, 1, 2, 3, 4, 5, 6, 7, 8,
    11, 12,
    14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
    # Two are perfectly valid characters but go wrong for different reasons.
    # 38 is '&' which gives: xmlParseEntityRef: no name.
    # 60 is '<' which gives: StartTag: invalid element namea different error.
]
BAD_BASESTRING_CHARS = [chr(b) for b in BAD]
BAD_UNICODE_CHARS = [unichr(b) for b in BAD]

然后像这样使用它:

def remove_bad_chars(value):
    # Remove bad control characters.
    if isinstance(value, unicode):
        for char in BAD_UNICODE_CHARS:
            value = value.replace(char, u'')
    elif isinstance(value, basestring):
        for char in BAD_BASESTRING_CHARS:
            value = value.replace(char, '')
    return value

如果value 2千兆字节,您可能需要以更有效的方式执行此操作,但我忽略了这一点,尽管问题提到了它。 在我的情况下,我是创建xml文件的人,但是我需要在原始数据中处理这些字符,所以在将数据放入xml之前我将使用此函数。

从谷歌找到这个帖子,而@ Michael的答案最终引导我找到一个解决方案(至少我的问题)我想在这里提供更多的复制/粘贴答案,以解决可以简单解决的问题:

from lxml import etree

# Create a parser
parser = etree.XMLParser(recover=True)

parsed_file = etree.parse('/path/to/your/janky/xml/file.xml', parser=parser)

我遇到了一个问题,我无法控制XML预处理,并且正在获得一个包含无效字符的文件。 @Michael的答案继续详细阐述了一种处理无效字符的方法,其中recover=True无法解决。 幸运的是,这足以让事情继续前进。

codecs Python模块提供一个EncodedFile类,作为文件的包装器 - 您应该将此类的对象传递给lxml,设置为使用XML char实体替换未知字符 -

试着这样做:

from lxml import etree
import codecs

enc_file = codecs.EncodedFile(file("my_file.xml"), "ASCII", "ASCII", "xmlcharrefreplace")

etparse = etree.iterparse(enc_file, events=("start",), encoding="CP1252")
...

传递的“xmlcharrefreplace”常量是“errors”参数,并指定如何处理未知字符。 它可能是“严格”(引发错误),“忽略”(保持原样),“替换”(将char替换为“?”),“xmlrefreplace”(创建“&#xxxx;”xml引用)或“ backslahreplace“(创建一个Python有效的反斜杠引用)。 有关更多信息,请查看: http//docs.python.org/library/codecs.html

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM