为什么这个特定的 xml 解析代码对小 xml 有效,但对大 xml 无效?

Why this particular xml parsing code works for small xml, but fails for large xml?

我有一个解析 xml 内容的函数,如下所示:

def parse_individual_xml(self, xml_url):
    xml_data_to_parse = urlopen(xml_url).read()
    jobs = ET.fromstring(xml_data_to_parse)
    return jobs

在我处理较小的文件 (1-2 mb) 之前,此功能运行良好。但是当我取了一个大的 xml url 时,我得到了这个错误。

xml.etree.ElementTree.ParseError: not well-formed (invalid token): line 1, column 0

Afaik,这是一些编码解码问题。

下面的函数给出了完全相同的行为。

def parse_individual_xml(self, xml_url):
    xml_data_to_parse = urlopen(xml_url)
    jobs = ET.parse(xml_data_to_parse).getroot()
    return jobs

然后我尝试了一些不同的方法。

我在本地下载了那个大文件,并更改了如下功能:

def parse_individual_xml(self, xml_local_path):
    jobs = ET.parse(xml_local_path).getroot()
    return jobs

而且,它适用于任何大小的文件。最终我会使用etree的iterparse。但起初我想知道上述行为的原因。

我该如何解决?

几乎可以肯定远程服务器 compressing large responses 使用 GZIP(或者,不太常见,deflate)。

基于 Content-Encoding header,在尝试解析流之前解压缩流:

import gzip

response = urlopen(xml_url)
if response.info().get('Content-Encoding') == 'gzip':
    # transparent decompression of a GZIP-ed response
    response = gzip.GzipFile(fileobj=response)
jobs = ET.parse(response).getroot()

您可能需要考虑改用 requests 库,它可以透明地为您处理。要将数据 到迭代解析器,请使用 stream=True,访问 response.raw file-like object 并将其配置为进行透明解压缩:

response = requests.get(xml_url, stream=True)
response.raw.decode_content = True  # handle content-encoding compression
jobs = ET.parse(response.raw).getroot()  # or use iterparse