Skip to main content

20.13. xml.parsers.expat —使用Expat快速XML解析


警告

pyexpat 模块对于恶意构造的数据不安全。如果需要解析不受信任或未经身份验证的数据,请参阅 XML漏洞

xml.parsers.expat 模块是Expat非验证XML解析器的Python接口。模块提供单个扩展类型 xmlparser,它表示XML解析器的当前状态。创建 xmlparser 对象后,可以将对象的各种属性设置为处理函数。当XML文档然后被馈送到解析器时,为XML文档中的字符数据和标记调用处理程序函数。

此模块使用 pyexpat 模块提供对Expat解析器的访问。不推荐直接使用 pyexpat 模块。

此模块提供一个异常和一个类型对象:

exception xml.parsers.expat.ExpatError

当Expat报告错误时引发异常。有关解释Expat错误的更多信息,请参阅 ExpatError异常 部分。

exception xml.parsers.expat.error

ExpatError 别名。

xml.parsers.expat.XMLParserType

来自 ParserCreate() 函数的返回值的类型。

xml.parsers.expat 模块包含两个功能:

xml.parsers.expat.ErrorString(errno)

返回给定错误编号 errno 的说明字符串。

xml.parsers.expat.ParserCreate(encoding=None, namespace_separator=None)

创建并返回一个新的 xmlparser 对象。如果指定,encoding 必须是命名XML数据使用的编码的字符串。 Expat不支持与Python一样多的编码,它的编码库不能扩展;它支持UTF-8,UTF-16,ISO-8859-1(Latin1)和ASCII。如果给定 encoding [1],它将覆盖文档的隐式或显式编码。

Expat可以为您选择执行XML命名空间处理,通过为 namespace_separator 提供值来启用。该值必须是单字符字符串;如果字符串具有非法长度(None 被认为与省略相同),则 ValueError 将被引发。启用命名空间处理时,将扩展属于命名空间的元素类型名称和属性名称。传递给元素处理程序 StartElementHandlerEndElementHandler 的元素名称将是命名空间URI,命名空间分隔符字符和名称的本地部分的连接。如果命名空间分隔符是一个零字节(chr(0)),那么命名空间URI和本地部分将被连接而没有任何分隔符。

例如,如果 namespace_separator 设置为空格字符(' ')并解析以下文档:

<?xml version="1.0"?>
<root xmlns    = "http://default-namespace.org/"
      xmlns:py = "http://www.python.org/ns/">
  <py:elem1 />
  <elem2 xmlns="" />
</root>

StartElementHandler 将为每个元素接收以下字符串:

http://default-namespace.org/ root
http://www.python.org/ns/ elem1
elem2

由于 pyexpat 使用的 Expat 库的限制,返回的 xmlparser 实例只能用于解析单个XML文档。为每个文档调用 ParserCreate 以提供唯一的解析器实例。

参见

Expat XML解析器

Expat项目的主页。

20.13.1. XMLParser对象

xmlparser 对象有以下方法:

xmlparser.Parse(data[, isfinal])

解析字符串 data 的内容,调用适当的处理函数来处理解析的数据。 isfinal 必须在最终调用此方法时为true;它允许在片段中解析单个文件,而不是提交多个文件。 data 可以是任何时候的空字符串。

xmlparser.ParseFile(file)

解析从对象 file 读取的XML数据。 file 只需要提供 read(nbytes) 方法,在没有更多数据时返回空字符串。

xmlparser.SetBase(base)

设置用于解析声明中系统标识符中的相对URI的基址。解析相对标识符留给应用程序:该值将作为 base 参数传递给 ExternalEntityRefHandler()NotationDeclHandler()UnparsedEntityDeclHandler() 函数。

xmlparser.GetBase()

返回一个字符串,该字符串包含先前对 SetBase() 的调用的基本集,如果 SetBase() 未被调用,则返回 None

xmlparser.GetInputContext()

以字符串形式返回生成当前事件的输入数据。数据位于包含文本的实体的编码中。当在事件处理程序未激活时调用,返回值为 None

xmlparser.ExternalEntityParserCreate(context[, encoding])

创建“子”解析器,可用于解析由父解析器解析的内容引用的外部解析实体。 context 参数应该是传递到 ExternalEntityRefHandler() 处理函数的字符串,如下所述。创建子解析器时,将 ordered_attributesspecified_attributes 设置为此解析器的值。

xmlparser.SetParamEntityParsing(flag)

参数实体的控制解析(包括外部DTD子集)。可能的 flag 值为 XML_PARAM_ENTITY_PARSING_NEVERXML_PARAM_ENTITY_PARSING_UNLESS_STANDALONEXML_PARAM_ENTITY_PARSING_ALWAYS。如果设置标志成功,则返回true。

xmlparser.UseForeignDTD([flag])

使用 flag 的真值(默认值)调用此方法将导致Expat使用 None 调用所有参数的 ExternalEntityRefHandler,以允许加载备用DTD。如果文档不包含文档类型声明,ExternalEntityRefHandler 将仍然被调用,但是 StartDoctypeDeclHandlerEndDoctypeDeclHandler 将不被调用。

flag 传递false值将取消传递true值的以前的调用,但是否则不起作用。

此方法只能在调用 Parse()ParseFile() 方法之前调用;在它们被调用之后调用它们导致 ExpatError 被提出,code 属性被设置为 errors.codes[errors.XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING]

xmlparser 对象具有以下属性:

xmlparser.buffer_size

buffer_text 为真时使用的缓冲区大小。可以通过为此属性分配一个新的整数值来设置新的缓冲区大小。当大小改变时,缓冲区将被刷新。

xmlparser.buffer_text

将此设置为true将导致 xmlparser 对象缓冲Expat返回的文本内容,以避免在可能的情况下多次调用 CharacterDataHandler() 回调。这可以大大提高性能,因为Expat通常在每行结尾将字符数据分成块。默认情况下,此属性为false,并且可以随时更改。

xmlparser.buffer_used

如果启用了 buffer_text,则存储在缓冲区中的字节数。这些字节表示UTF-8编码的文本。当 buffer_text 为假时,此属性没有有意义的解释。

xmlparser.ordered_attributes

将此属性设置为非零整数会导致将属性报告为列表而不是字典。属性按文档文本中找到的顺序显示。对于每个属性,呈现两个列表条目:属性名称和属性值。 (此模块的旧版本也使用此格式。)默认情况下,此属性为false;它可以随时更改。

xmlparser.specified_attributes

如果设置为非零整数,解析器将仅报告在文档实例中指定的那些属性,而不报告从属性声明派生的那些属性。设置它的应用程序需要特别小心,以便根据需要使用来自声明的其他信息,以符合XML处理器行为的标准。默认情况下,此属性为false;它可以随时更改。

以下属性包含与 xmlparser 对象遇到的最近错误相关的值,并且只有在对 Parse()ParseFile() 的调用引发 xml.parsers.expat.ExpatError 异常时才具有正确的值。

xmlparser.ErrorByteIndex

发生错误的字节索引。

xmlparser.ErrorCode

指定问题的数字代码。该值可以传递到 ErrorString() 函数,或与 errors 对象中定义的常量之一进行比较。

xmlparser.ErrorColumnNumber

发生错误的列号。

xmlparser.ErrorLineNumber

出现错误的行号。

以下属性包含与 xmlparser 对象中当前解析位置相关的值。在回调报告解析事件期间,它们指示生成事件的字符序列中的第一个的位置。当在回调之外调用时,指示的位置将刚好超过最后一个解析事件(不管是否存在相关联的回调)。

xmlparser.CurrentByteIndex

解析器输入中的当前字节索引。

xmlparser.CurrentColumnNumber

解析器输入中的当前列号。

xmlparser.CurrentLineNumber

解析器输入中的当前行号。

这里是可以设置的处理程序的列表。要在 xmlparser 对象 o 上设置处理程序,请使用 o.handlername = funchandlername 必须取自以下列表,func 必须是接受正确数量的参数的可调用对象。除非另有说明,参数都是字符串。

xmlparser.XmlDeclHandler(version, encoding, standalone)

在解析XML声明时调用。 XML声明是XML推荐的适用版本,文档文本的编码和可选的“独立”声明的(可选)声明。 versionencoding 将是字符串,并且如果文档被声明为独立的,则 standalone 将是 1,如果声明不是独立的,则 standalone 将是 0,如果省略了独立的子句,则 standalone 将是 -1。这仅适用于Expat版本1.95.0或更高版本。

xmlparser.StartDoctypeDeclHandler(doctypeName, systemId, publicId, has_internal_subset)

当Expat开始解析文档类型声明(<!DOCTYPE ...)时调用。 doctypeName 完全按照提供的方式提供。 systemIdpublicId 参数给出系统和公共标识符(如果指定),或 None (如果省略)。如果文档包含和内部文档声明子集,has_internal_subset 将为true。这需要Expat版本1.2或更高版本。

xmlparser.EndDoctypeDeclHandler()

当Expat完成解析文档类型声明时调用。这需要Expat版本1.2或更高版本。

xmlparser.ElementDeclHandler(name, model)

每个元素类型声明调用一次。 name 是元素类型的名称,model 是内容模型的表示。

xmlparser.AttlistDeclHandler(elname, attname, type, default, required)

为元素类型的每个声明的属性调用。如果一个属性列表声明声明了三个属性,这个处理程序被调用三次,每个属性一次。 elname 是声明所应用的元素的名称,attname 是声明的属性的名称。属性类型是作为 type 传递的字符串;可能的值为 'CDATA''ID''IDREF',... default 给出当文档实例未指定属性时使用的属性的默认值,如果没有默认值(#IMPLIED 值),则为 None。如果需要在文档实例中给出属性,则 required 将为true。这需要Expat版本1.95.0或更高版本。

xmlparser.StartElementHandler(name, attributes)

调用每个元素的开始。 name 是包含元素名称的字符串,attributes 是元素属性。如果 ordered_attributes 为真,这是一个列表(完整描述参见 ordered_attributes)。否则,它是一个字典将名称映射到值。

xmlparser.EndElementHandler(name)

调用每个元素的结束。

xmlparser.ProcessingInstructionHandler(target, data)

调用每个处理指令。

xmlparser.CharacterDataHandler(data)

调用字符数据。这将被称为正常字符数据,CDATA标记的内容和可忽略的空格。必须区分这些情况的应用程序可以使用 StartCdataSectionHandlerEndCdataSectionHandlerElementDeclHandler 回调来收集所需的信息。

xmlparser.UnparsedEntityDeclHandler(entityName, base, systemId, publicId, notationName)

调用未解析(NDATA)实体声明。这只适用于Expat库的1.2版本;对于更近的版本,请使用 EntityDeclHandler。 (Expat库中的基础函数已被声明为过时。)

xmlparser.EntityDeclHandler(entityName, is_parameter_entity, value, base, systemId, publicId, notationName)

调用所有实体声明。对于参数和内部实体,value 将是一个给出实体声明内容的字符串;这将是外部实体的 None。对于解析实体,notationName 参数将是 None,对于未解析实体,notationName 参数的名称将是。如果实体是参数实体,则 is_parameter_entity 将为真,而对于一般实体为假(大多数应用仅需要关注一般实体)。这只能从版本1.95.0的Expat库开始提供。

xmlparser.NotationDeclHandler(notationName, base, systemId, publicId)

调用符号声明。 notationNamebasesystemId,和 publicId 是字符串(如果给出)。如果省略公共标识符,则 publicId 将是 None

xmlparser.StartNamespaceDeclHandler(prefix, uri)

当元素包含命名空间声明时调用。在为放置声明的元素调用 StartElementHandler 之前,处理命名空间声明。

xmlparser.EndNamespaceDeclHandler(prefix)

当到达包含命名空间声明的元素的结束标记时调用。对于元素上的每个命名空间声明,调用一次,与调用 StartNamespaceDeclHandler 的顺序相反,以指示每个命名空间声明的范围的开始。对该处理程序的调用在对应于元素结束的 EndElementHandler 之后进行。

xmlparser.CommentHandler(data)

调用评论。 data 是注释的文本,不包括领先的 '<!- -' 和尾随 '- ->'

xmlparser.StartCdataSectionHandler()

在CDATA节开始时调用。需要这个和 EndCdataSectionHandler 以能够识别CDATA段的句法开始和结束。

xmlparser.EndCdataSectionHandler()

在CDATA部分的末尾调用。

xmlparser.DefaultHandler(data)

调用XML文档中未指定任何适用处理程序的任何字符。这意味着构造的一部分的字符可以被报告,但是没有提供处理程序。

xmlparser.DefaultHandlerExpand(data)

这与 DefaultHandler() 相同,但不抑制内部实体的扩展。实体引用不会传递给默认处理程序。

xmlparser.NotStandaloneHandler()

如果XML文档尚未声明为独立文档,则调用此方法。当有一个外部子集或对参数实体的引用时,会发生这种情况,但是XML声明中没有将XML声明设置为独立于 yes。如果此处理程序返回 0,则解析器将引发 XML_ERROR_NOT_STANDALONE 错误。如果未设置此处理程序,则此条件的解析器不会引发异常。

xmlparser.ExternalEntityRefHandler(context, base, systemId, publicId)

调用对外部实体的引用。 base 是由之前对 SetBase() 的调用设置的当前基数。公共和系统标识符 systemIdpublicId 是字符串,如果给出的话;如果未给出公共标识符,则 publicId 将是 Nonecontext 值是不透明的,并且应仅如下所述使用。

对于要解析的外部实体,必须实现此处理程序。它负责使用 ExternalEntityParserCreate(context) 创建子解析器,使用适当的回调初始化它,并解析实体。这个处理程序应该返回一个整数;如果返回 0,解析器将引发 XML_ERROR_EXTERNAL_ENTITY_HANDLING 错误,否则解析将继续。

如果不提供此处理程序,外部实体由 DefaultHandler 回调(如果提供)报告。

20.13.2. ExpatError异常

ExpatError 异常有一些有趣的属性:

ExpatError.code

Expat的特定错误的内部错误编号。 errors.messages 字典将这些错误号映射到Expat的错误消息。例如:

from xml.parsers.expat import ParserCreate, ExpatError, errors

p = ParserCreate()
try:
    p.Parse(some_xml_document)
except ExpatError as err:
    print("Error:", errors.messages[err.code])

errors 模块还提供错误消息常量和字典 codes 将这些消息映射回错误代码,见下文。

ExpatError.lineno

检测到错误的行号。第一行编号为 1

ExpatError.offset

字符偏移到发生错误的行。第一列的编号为 0

20.13.3. 例

下面的程序定义了三个处理程序,只是打印出它们的参数。

import xml.parsers.expat

# 3 handler functions
def start_element(name, attrs):
    print('Start element:', name, attrs)
def end_element(name):
    print('End element:', name)
def char_data(data):
    print('Character data:', repr(data))

p = xml.parsers.expat.ParserCreate()

p.StartElementHandler = start_element
p.EndElementHandler = end_element
p.CharacterDataHandler = char_data

p.Parse("""<?xml version="1.0"?>
<parent id="top"><child1 name="paul">Text goes here</child1>
<child2 name="fred">More text</child2>
</parent>""", 1)

这个程序的输出是:

Start element: parent {'id': 'top'}
Start element: child1 {'name': 'paul'}
Character data: 'Text goes here'
End element: child1
Character data: '\n'
Start element: child2 {'name': 'fred'}
Character data: 'More text'
End element: child2
Character data: '\n'
End element: parent

20.13.4. 内容模型描述

使用嵌套元组描述内容模型。每个元组包含四个值:类型,量词,名称和子元组。孩子只是额外的内容模型描述。

前两个字段的值是在 xml.parsers.expat.model 模块中定义的常量。这些常量可以分为两组:模型类型组和量词组。

模型类型组中的常量为:

xml.parsers.expat.model.XML_CTYPE_ANY

由模型名称命名的元素被声明为具有 ANY 的内容模型。

xml.parsers.expat.model.XML_CTYPE_CHOICE

命名的元素允许从多个选项中进行选择;这用于内容模型如 (A | B | C)

xml.parsers.expat.model.XML_CTYPE_EMPTY

声明为 EMPTY 的元素具有此模型类型。

xml.parsers.expat.model.XML_CTYPE_MIXED
xml.parsers.expat.model.XML_CTYPE_NAME
xml.parsers.expat.model.XML_CTYPE_SEQ

用该模型类型表示代表一个接一个跟随的一系列模型的模型。这用于 (A, B, C) 等型号。

量词组中的常数为:

xml.parsers.expat.model.XML_CQUANT_NONE

没有给出修改器,因此它可以正好出现一次,如 A

xml.parsers.expat.model.XML_CQUANT_OPT

模型是可选的:它可以出现一次或根本不出现,如 A?

xml.parsers.expat.model.XML_CQUANT_PLUS

模型必须发生一次或多次(如 A+)。

xml.parsers.expat.model.XML_CQUANT_REP

模型必须发生零次或多次,如 A*

20.13.5. Expat误差常数

xml.parsers.expat.errors 模块中提供了以下常量。这些常量在解释发生错误时引发的 ExpatError 异常对象的一些属性非常有用。由于向后兼容性原因,常数的值是错误 message,而不是数字错误 code,您通过比较其 code 属性与 errors.codes[errors.XML_ERROR_CONSTANT_NAME] 来做到这一点。

errors 模块具有以下属性:

xml.parsers.expat.errors.codes

将数字错误代码映射到其字符串描述的字典。

3.2 新版功能.

xml.parsers.expat.errors.messages

将字符串描述映射到其错误代码的字典。

3.2 新版功能.

xml.parsers.expat.errors.XML_ERROR_ASYNC_ENTITY
xml.parsers.expat.errors.XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF

引用到外部实体而不是内部实体的属性值中的实体引用。

xml.parsers.expat.errors.XML_ERROR_BAD_CHAR_REF

字符引用指的是XML中非法的字符(例如,字符 0 或’&#0;‘)。

xml.parsers.expat.errors.XML_ERROR_BINARY_ENTITY_REF

实体引用指的是用符号声明的实体,因此不能被解析。

xml.parsers.expat.errors.XML_ERROR_DUPLICATE_ATTRIBUTE

一个属性在开始标记中多次使用。

xml.parsers.expat.errors.XML_ERROR_INCORRECT_ENCODING
xml.parsers.expat.errors.XML_ERROR_INVALID_TOKEN

在输入字节无法正确分配给字符时引发;例如,UTF-8输入流中的NUL字节(值 0)。

xml.parsers.expat.errors.XML_ERROR_JUNK_AFTER_DOC_ELEMENT

文档元素之后出现了除空格之外的其他内容。

xml.parsers.expat.errors.XML_ERROR_MISPLACED_XML_PI

在除了输入数据的开始之外的某处发现了XML声明。

xml.parsers.expat.errors.XML_ERROR_NO_ELEMENTS

文档不包含元素(XML要求所有文档只包含一个顶级元素)。

xml.parsers.expat.errors.XML_ERROR_NO_MEMORY

Expat无法在内部分配内存。

xml.parsers.expat.errors.XML_ERROR_PARAM_ENTITY_REF

发现参数实体引用不允许的地方。

xml.parsers.expat.errors.XML_ERROR_PARTIAL_CHAR

在输入中找到不完整的字符。

xml.parsers.expat.errors.XML_ERROR_RECURSIVE_ENTITY_REF

实体引用包含对同一实体的另一引用;可能通过不同的名称,并且可能间接。

xml.parsers.expat.errors.XML_ERROR_SYNTAX

遇到一些未指定的语法错误。

xml.parsers.expat.errors.XML_ERROR_TAG_MISMATCH

结束标记与最内部的开始标记不匹配。

xml.parsers.expat.errors.XML_ERROR_UNCLOSED_TOKEN

某些令牌(例如开始标签)在流结束或遇到下一个令牌之前未关闭。

xml.parsers.expat.errors.XML_ERROR_UNDEFINED_ENTITY

引用未定义的实体。

xml.parsers.expat.errors.XML_ERROR_UNKNOWN_ENCODING

Expat不支持文档编码。

xml.parsers.expat.errors.XML_ERROR_UNCLOSED_CDATA_SECTION

未关闭标有CDATA的部分。

xml.parsers.expat.errors.XML_ERROR_EXTERNAL_ENTITY_HANDLING
xml.parsers.expat.errors.XML_ERROR_NOT_STANDALONE

解析器确定文档不是“独立的”,尽管它声明自己在XML声明中,并且 NotStandaloneHandler 已设置并返回 0

xml.parsers.expat.errors.XML_ERROR_UNEXPECTED_STATE
xml.parsers.expat.errors.XML_ERROR_ENTITY_DECLARED_IN_PE
xml.parsers.expat.errors.XML_ERROR_FEATURE_REQUIRES_XML_DTD

需要一个操作,需要编译DTD支持,但是在没有DTD支持的情况下配置了Expat。这不应该由 xml.parsers.expat 模块的标准构建报告。

xml.parsers.expat.errors.XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING

在解析开始后请求行为更改,只能在解析开始前更改。这是(目前)只是由 UseForeignDTD() 提出。

xml.parsers.expat.errors.XML_ERROR_UNBOUND_PREFIX

启用命名空间处理时发现未声明前缀。

xml.parsers.expat.errors.XML_ERROR_UNDECLARING_PREFIX

该文档试图删除与前缀相关联的名称空间声明。

xml.parsers.expat.errors.XML_ERROR_INCOMPLETE_PE

参数实体包含不完整的标记。

xml.parsers.expat.errors.XML_ERROR_XML_DECL

文档根本没有文档元素。

xml.parsers.expat.errors.XML_ERROR_TEXT_DECL

解析外部实体中的文本声明时出错。

xml.parsers.expat.errors.XML_ERROR_PUBLICID

在公共ID中找到不允许的字符。

xml.parsers.expat.errors.XML_ERROR_SUSPENDED

请求的操作是在暂停的解析器上进行的,但不允许。这包括尝试提供额外的输入或停止解析器。

xml.parsers.expat.errors.XML_ERROR_NOT_SUSPENDED

当解析器未被挂起时,尝试恢复解析器。

xml.parsers.expat.errors.XML_ERROR_ABORTED

这不应该报告给Python应用程序。

xml.parsers.expat.errors.XML_ERROR_FINISHED

请求的操作是在解析输入完成的解析器上进行的,但不允许。这包括尝试提供额外的输入或停止解析器。

xml.parsers.expat.errors.XML_ERROR_SUSPEND_PE

脚注

[1]

XML输出中包含的编码字符串应符合相应的标准。例如,“UTF-8”有效,但“UTF8”不是。见 https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-EncodingDeclhttps://www.iana.org/assignments/character-sets/character-sets.xhtml