# 20.13. `xml.parsers.expat` - 使用Expat

警告

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

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

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

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

exception `xml.parsers.expat.``ExpatError`

  当Expat报告错误时引发异常。有关解释Expat错误的更多信息，请参阅ExpatError Exceptions一节。

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提供值来启用。The value must be a one-character string; a `ValueError` will be raised if the string has an illegal length (`None` is considered the same as omission). 启用命名空间处理时，将扩展属于命名空间的元素类型名称和属性名称。传递给元素处理程序`StartElementHandler`和`EndElementHandler`的元素名称将是命名空间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 Objects

`xmlparser`对象具有以下方法：

`xmlparser.``Parse`(data[, isfinal])

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

`xmlparser.``ParseFile`(file)

  解析对象文件中的XML数据读取。文件只需提供`read(nbytes)`方法，当没有更多数据时返回空字符串。

`xmlparser.``SetBase`(base)

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

`xmlparser.``GetBase`()

  如果`SetBase()`未被调用，则返回一个包含先前调用`SetBase()`或`None`

`xmlparser.``GetInputContext`()

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

`xmlparser.``ExternalEntityParserCreate`(context[, encoding])

  创建一个“子”解析器，可用于解析由父解析器解析的内容引用的外部解析实体。上下文参数应该是传递给`ExternalEntityRefHandler()`处理函数的字符串，如下所述。使用设置为此解析器的值的`ordered_attributes`和`specified_attributes`创建子解析器。

`xmlparser.``SetParamEntityParsing`(flag)

  参数实体的控制解析（包括外部DTD子集）。可能的标志值为`XML_PARAM_ENTITY_PARSING_NEVER`，`XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE`和`XML_PARAM_ENTITY_PARSING_ALWAYS`。如果设置标志成功，则返回true。

`xmlparser.``UseForeignDTD`([flag])

  使用标志（默认值）的真值调用此函数将导致Expat使用`None`为所有参数调用`ExternalEntityRefHandler`，以允许备用DTD加载。如果文档不包含文档类型声明，则仍会调用`ExternalEntityRefHandler`，但不会调用`StartDoctypeDeclHandler`和`EndDoctypeDeclHandler`。

  为标志传递false值将取消传递true值的以前调用，但其他方式没有效果。

  This method can only be called before the `Parse()` or `ParseFile()` methods are called; calling it after either of those have been called causes `ExpatError` to be raised with the `code` attribute set to `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`为false时，此属性没有有意义的解释。

`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 = func` 。handlername必须取自以下列表，func必须是接受正确数量参数的可调用对象。除非另有说明，参数都是字符串。

`xmlparser.``XmlDeclHandler`(version, encoding, standalone)

  在解析XML声明时调用。XML声明是XML推荐的适用版本的声明（可选），文档文本的编码以及可选的“独立”声明。version and encoding will be strings, and standalone will be `1` if the document is declared standalone, `0` if it is declared not to be standalone, or `-1` if the standalone clause was omitted. 这仅适用于Expat版本1.95.0或更高版本。

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

  Expat开始解析文档类型声明（`＆lt；！DOCTYPE ...`）时调用。提供的doctypeName与提供的完全相同。systemId和publicId参数指定系统和公共标识符，如果省略，则`None`。如果文档包含内部文档声明子集，则has_internal_subset将为true。这需要Expat版本1.2或更高版本。

`xmlparser.``EndDoctypeDeclHandler`()

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

`xmlparser.``ElementDeclHandler`(name, model)

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

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

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

`xmlparser.``StartElementHandler`(name, attributes)

  调用每个元素的开始。name是包含元素名称的字符串，属性是元素属性。如果`ordered_attributes`为true，则这是一个列表（有关完整说明，请参阅`ordered_attributes`）。否则，它是一个字典将名称映射到值。

`xmlparser.``EndElementHandler`(name)

  调用每个元素的结尾。

`xmlparser.``ProcessingInstructionHandler`(target, data)

  调用每个处理指令。

`xmlparser.``CharacterDataHandler`(data)

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

`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`，以及未解析实体的符号名称。如果实体是参数实体，则is_parameter_entity将为true，对于一般实体为false（大多数应用程序只需要关注一般实体）。这只能从Expat库的1.95.0版本开始提供。

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

  调用符号声明。notationName，base和systemId和publicId都是字符串。如果省略公共标识符，publicId将为`None`。

`xmlparser.``StartNamespaceDeclHandler`(prefix, uri)

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

`xmlparser.``EndNamespaceDeclHandler`(prefix)

  当到达包含命名空间声明的元素的结束标记时调用。对于调用`StartNamespaceDeclHandler`的顺序的反向，对元素上的每个命名空间声明调用一次，以指示每个命名空间声明的作用域的开始。对此处理程序的调用在对应的`EndElementHandler`之后作为元素的结尾。

`xmlparser.``CommentHandler`(data)

  调用评论。data is the text of the comment, excluding the leading `'<!-``-'` and trailing `'-``->'`.

`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()`设置的当前基数。公共和系统标识符systemId和publicId是字符串，如果给定的话；如果未给出公共标识符，则publicId将为`None`。上下文值是不透明的，并且应该仅按如下所述使用。

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

  如果未提供此处理程序，则通过`DefaultHandler`回调（如果提供）报告外部实体。

## 20.13.2. ExpatError Exceptions

`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. Example

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

    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. Content Model Descriptions

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

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

模型类型组中的常量为：

`xml.parsers.expat.model。` `XML_CTYPE_ANY`

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

`xml.parsers.expat.model。` `XML_CTYPE_CHOICE`

  命名的元素允许从多个选项中进行选择；这用于内容模型，例如`（A | B | C） / t5>`。

`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 error constants

以下常量在`xml.parsers.expat.errors`模块中提供。这些常量在解释发生错误时引发的`ExpatError`异常对象的某些属性非常有用。由于向后兼容性原因，常量的值是错误消息而不是数字错误代码，您可以通过比较其`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-EncodingDecl和https://www.iana.org/assignments/character- sets / character-sets.xhtml。