19.15. xml.parsers.expat — 使用 Expat 的快速 XML 解析 — Python 文档
19.15. xml.parsers.expat — 使用 Expat 的快速 XML 解析
2.0 版中的新功能。
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[, namespace_separator]])
创建并返回一个新的
xmlparser
对象。 encoding(如果指定)必须是命名 XML 数据使用的编码的字符串。 Expat 不支持像 Python 那样多的编码,并且它的编码库无法扩展; 它支持 UTF-8、UTF-16、ISO-8859-1 (Latin1) 和 ASCII。 如果给出 encoding 1,它将覆盖文档的隐式或显式编码。Expat 可以选择为您执行 XML 命名空间处理,通过为 namespace_separator 提供值来启用。 该值必须是一个单字符的字符串; 如果字符串具有非法长度(
None
被视为与省略相同),则会引发ValueError
。 启用命名空间处理时,将扩展属于命名空间的元素类型名称和属性名称。 传递给元素处理程序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
以提供唯一的解析器实例。
19.15.1. XMLParser 对象
xmlparser
对象有以下方法:
- xmlparser.Parse(data[, isfinal])
- 解析字符串 data 的内容,调用适当的处理函数来处理解析的数据。 isfinal 在最终调用此方法时必须为真; 它允许在片段中解析单个文件,而不是提交多个文件。 data 可以随时为空字符串。
- xmlparser.ParseFile(file)
- 解析从对象 文件 读取的 XML 数据。 file 只需要提供
read(nbytes)
方法,当没有更多数据时返回空字符串。
- xmlparser.SetBase(base)
- 设置用于解析声明中系统标识符中的相对 URI 的基数。 解析相对标识符留给应用程序:该值将作为 base 参数传递给 ExternalEntityRefHandler()、NotationDeclHandler() 和 ]UnparsedEntityDeclHandler() 函数。
- xmlparser.GetInputContext()
以字符串形式返回生成当前事件的输入数据。 数据采用包含文本的实体的编码。 在事件处理程序未激活时调用时,返回值为
None
。2.1 版中的新功能。
- xmlparser.ExternalEntityParserCreate(context[, encoding])
- 创建一个“子”解析器,可用于解析由父解析器解析的内容引用的外部解析实体。 context 参数应该是传递给 ExternalEntityRefHandler() 处理函数的字符串,如下所述。 创建子解析器时,将 ordered_attributes、returns_unicode 和 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])
使用 flag(默认值)的真值调用此方法将导致 Expat 调用 ExternalEntityRefHandler 和 None 的所有参数,以允许加载备用 DTD . 如果文档不包含文档类型声明,仍会调用 ExternalEntityRefHandler,但不会调用 StartDoctypeDeclHandler 和 EndDoctypeDeclHandler。
为 flag 传递假值将取消先前传递真值的调用,否则无效。
此方法只能在调用 Parse() 或 ParseFile() 方法之前调用; 在其中任何一个被调用后调用它会导致 ExpatError 被引发,code 属性设置为
errors.XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING
。2.3 版中的新功能。
xmlparser
对象具有以下属性:
- xmlparser.buffer_size
buffer_text 为真时使用的缓冲区大小。 可以通过为该属性分配新的整数值来设置新的缓冲区大小。 当大小改变时,缓冲区将被刷新。
2.3 版中的新功能。
2.6 版更改: 现在可以更改缓冲区大小。
- xmlparser.buffer_text
将此设置为 true 会导致
xmlparser
对象缓冲由 Expat 返回的文本内容,以避免尽可能多次调用 CharacterDataHandler() 回调。 这可以显着提高性能,因为 Expat 通常在每一行结尾处将字符数据分成块。 此属性默认为 false,并且可以随时更改。2.3 版中的新功能。
- xmlparser.buffer_used
如果启用 buffer_text,则缓冲区中存储的字节数。 这些字节表示 UTF-8 编码的文本。 当 buffer_text 为 false 时,此属性没有有意义的解释。
2.3 版中的新功能。
- xmlparser.ordered_attributes
将此属性设置为非零整数会导致将属性报告为列表而不是字典。 属性按文档文本中的顺序显示。 对于每个属性,显示两个列表条目:属性名称和属性值。 (此模块的旧版本也使用此格式。)默认情况下,此属性为 false; 它可以随时更改。
2.1 版中的新功能。
- xmlparser.returns_unicode
如果此属性设置为非零整数,则处理程序函数将传递 Unicode 字符串。 如果 returns_unicode 是 False,则包含 UTF-8 编码数据的 8 位字符串将被传递给处理程序。 当 Python 使用 Unicode 支持构建时,默认为 True。
1.6 版更改: 可以随时更改以影响结果类型。
- xmlparser.specified_attributes
如果设置为非零整数,解析器将仅报告在文档实例中指定的那些属性,而不报告从属性声明派生的那些属性。 设置此项的应用程序需要特别小心,以根据需要使用声明中可用的附加信息,以符合 XML 处理器的行为标准。 默认情况下,此属性为 false; 它可以随时更改。
2.1 版中的新功能。
以下属性包含与 xmlparser
对象最近遇到的错误相关的值,并且只有在对 Parse()
或 ParseFile()
的调用引发 时才会有正确的值]xml.parsers.expat.ExpatError 异常。
- xmlparser.ErrorByteIndex
- 发生错误的字节索引。
- xmlparser.ErrorCode
- 指定问题的数字代码。 该值可以传递给 ErrorString() 函数,或与
errors
对象中定义的常量之一进行比较。
- xmlparser.ErrorColumnNumber
- 发生错误的列号。
- xmlparser.ErrorLineNumber
- 发生错误的行号。
以下属性包含与 xmlparser
对象中的当前解析位置相关的值。 在报告解析事件的回调期间,它们指示生成事件的字符序列中第一个的位置。 在回调之外调用时,指示的位置将刚好经过最后一个解析事件(无论是否有关联的回调)。
2.4 版中的新功能。
- xmlparser.CurrentByteIndex
- 解析器输入中的当前字节索引。
- xmlparser.CurrentColumnNumber
- 解析器输入中的当前列号。
- xmlparser.CurrentLineNumber
- 解析器输入中的当前行号。
这是可以设置的处理程序列表。 要在 xmlparser
对象 o 上设置处理程序,请使用 o.handlername = func
。 handlername 必须取自以下列表,并且 func 必须是接受正确数量参数的可调用对象。 除非另有说明,否则参数都是字符串。
- xmlparser.XmlDeclHandler(version, encoding, standalone)
在解析 XML 声明时调用。 XML 声明是 XML 建议的适用版本的(可选)声明、文档文本的编码和可选的“独立”声明。 version 和 encoding 将是 returns_unicode 属性指定的类型的字符串,并且 standalone 将是
1
如果文档被声明为独立的,0
如果它被声明为不是独立的,或者-1
如果省略了独立子句。 这仅适用于 Expat 版本 1.95.0 或更高版本。2.1 版中的新功能。
- xmlparser.StartDoctypeDeclHandler(doctypeName, systemId, publicId, has_internal_subset)
- 当 Expat 开始解析文档类型声明 (
<!DOCTYPE ...
) 时调用。 doctypeName 完全按照所提供的方式提供。 systemId 和 publicId 参数给出系统和公共标识符(如果指定),或None
如果省略。 has_internal_subset 如果文档包含内部文档声明子集,则为真。 这需要 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 给出了文档未指定属性时使用的属性的默认值实例,或None
如果没有默认值(#IMPLIED
值)。 如果需要在文档实例中给出该属性,则 required 将为 true。 这需要 Expat 版本 1.95.0 或更高版本。
- xmlparser.StartElementHandler(name, attributes)
- 调用每个元素的开始。 name 是包含元素名称的字符串,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 实体为参数实体时为真,一般实体为假(大多数应用只需要关心一般实体)。 这仅从 Expat 库的 1.95.0 版开始可用。2.1 版中的新功能。
- 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 是注释的文本,不包括开头的
'<!-
-'
和结尾的'-
->'
。
- 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
。 context 值是不透明的,只能按如下所述使用。对于要解析的外部实体,必须实现此处理程序。 它负责使用
ExternalEntityParserCreate(context)
创建子解析器,使用适当的回调对其进行初始化,并解析实体。 这个处理程序应该返回一个整数; 如果它返回0
,解析器将引发XML_ERROR_EXTERNAL_ENTITY_HANDLING
错误,否则解析将继续。如果未提供此处理程序,则外部实体将通过 DefaultHandler 回调(如果提供)报告。
19.15.2. ExpatError 异常
ExpatError 异常有许多有趣的属性:
- ExpatError.code
Expat 特定错误的内部错误编号。 这将匹配该模块的
errors
对象中定义的常量之一。2.1 版中的新功能。
- ExpatError.lineno
检测到错误的行号。 第一行编号为
1
。2.1 版中的新功能。
- ExpatError.offset
字符偏移到发生错误的行中。 第一列编号为
0
。2.1 版中的新功能。
19.15.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
19.15.4. 内容模型描述
使用嵌套元组描述内容模型。 每个元组包含四个值:类型、量词、名称和子元组。 子项只是附加的内容模型描述。
前两个字段的值是在 xml.parsers.expat 模块的 model
对象中定义的常量。 这些常量可以分为两组:模型类型组和量词组。
模型类型组中的常量是:
- xml.parsers.expat.XML_CTYPE_ANY
- 由模型名称命名的元素被声明为具有
ANY
的内容模型。
- xml.parsers.expat.XML_CTYPE_CHOICE
- 命名元素允许从多个选项中进行选择; 这用于内容模型,例如
(A | B | C)
。
- xml.parsers.expat.XML_CTYPE_EMPTY
- 声明为
EMPTY
的元素具有此模型类型。
- xml.parsers.expat.XML_CTYPE_MIXED
- xml.parsers.expat.XML_CTYPE_NAME
- xml.parsers.expat.XML_CTYPE_SEQ
- 代表一系列模型的模型用此模型类型表示。 用于
(A, B, C)
等型号。
量词组中的常量是:
- xml.parsers.expat.XML_CQUANT_NONE
- 没有给出修饰符,所以它只能出现一次,如
A
。
- xml.parsers.expat.XML_CQUANT_OPT
- 模型是可选的:它可以出现一次,也可以根本不出现,如
A?
。
- xml.parsers.expat.XML_CQUANT_PLUS
- 模型必须出现一次或多次(如
A+
)。
- xml.parsers.expat.XML_CQUANT_REP
- 模型必须出现零次或多次,如
A*
。
19.15.5. Expat 错误常量
xml.parsers.expat 模块的 errors
对象中提供了以下常量。 这些常量可用于解释发生错误时引发的 ExpatError 异常对象的某些属性。
errors
对象具有以下属性:
- xml.parsers.expat.XML_ERROR_ASYNC_ENTITY
- xml.parsers.expat.XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF
- 属性值中的实体引用引用外部实体而不是内部实体。
- xml.parsers.expat.XML_ERROR_BAD_CHAR_REF
- 字符引用指的是 XML 中非法的字符(例如,字符
0
或 '�
')。
- xml.parsers.expat.XML_ERROR_BINARY_ENTITY_REF
- 实体引用引用了用符号声明的实体,因此无法解析。
- xml.parsers.expat.XML_ERROR_DUPLICATE_ATTRIBUTE
- 一个属性在一个开始标签中被多次使用。
- xml.parsers.expat.XML_ERROR_INCORRECT_ENCODING
- xml.parsers.expat.XML_ERROR_INVALID_TOKEN
- 当输入字节无法正确分配给字符时引发; 例如,UTF-8 输入流中的 NUL 字节(值
0
)。
- xml.parsers.expat.XML_ERROR_JUNK_AFTER_DOC_ELEMENT
- 在文档元素之后出现了除空格以外的其他东西。
- xml.parsers.expat.XML_ERROR_MISPLACED_XML_PI
- 在输入数据的开头以外的其他地方发现了 XML 声明。
- xml.parsers.expat.XML_ERROR_NO_ELEMENTS
- 该文档不包含任何元素(XML 要求所有文档只包含一个顶级元素)。
- xml.parsers.expat.XML_ERROR_NO_MEMORY
- Expat 无法在内部分配内存。
- xml.parsers.expat.XML_ERROR_PARAM_ENTITY_REF
- 在不允许的地方找到了参数实体引用。
- xml.parsers.expat.XML_ERROR_PARTIAL_CHAR
- 在输入中发现不完整的字符。
- xml.parsers.expat.XML_ERROR_RECURSIVE_ENTITY_REF
- 实体引用包含对同一实体的另一个引用; 可能通过不同的名称,也可能是间接的。
- xml.parsers.expat.XML_ERROR_SYNTAX
- 遇到一些未指定的语法错误。
- xml.parsers.expat.XML_ERROR_TAG_MISMATCH
- 结束标签与最里面的开放开始标签不匹配。
- xml.parsers.expat.XML_ERROR_UNCLOSED_TOKEN
- 某些标记(例如开始标记)在流结束或遇到下一个标记之前未关闭。
- xml.parsers.expat.XML_ERROR_UNDEFINED_ENTITY
- 引用了一个未定义的实体。
- xml.parsers.expat.XML_ERROR_UNKNOWN_ENCODING
- Expat 不支持文档编码。
- xml.parsers.expat.XML_ERROR_UNCLOSED_CDATA_SECTION
- 未关闭 CDATA 标记部分。
- xml.parsers.expat.XML_ERROR_EXTERNAL_ENTITY_HANDLING
- xml.parsers.expat.XML_ERROR_NOT_STANDALONE
- 解析器确定该文档不是“独立的”,尽管它在 XML 声明中声明了自己,并且设置了
NotStandaloneHandler
并返回了0
。
- xml.parsers.expat.XML_ERROR_UNEXPECTED_STATE
- xml.parsers.expat.XML_ERROR_ENTITY_DECLARED_IN_PE
- xml.parsers.expat.XML_ERROR_FEATURE_REQUIRES_XML_DTD
- 请求了一个需要编译 DTD 支持的操作,但 Expat 被配置为没有 DTD 支持。 xml.parsers.expat 模块的标准构建永远不应该报告这种情况。
- xml.parsers.expat.XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING
- 解析开始后请求行为更改,只能在解析开始之前更改。 这(目前)仅由
UseForeignDTD()
引发。
- xml.parsers.expat.XML_ERROR_UNBOUND_PREFIX
- 启用命名空间处理时发现未声明的前缀。
- xml.parsers.expat.XML_ERROR_UNDECLARING_PREFIX
- 该文档试图删除与前缀关联的命名空间声明。
- xml.parsers.expat.XML_ERROR_INCOMPLETE_PE
- 参数实体包含不完整的标记。
- xml.parsers.expat.XML_ERROR_XML_DECL
- 该文档根本不包含文档元素。
- xml.parsers.expat.XML_ERROR_TEXT_DECL
- 解析外部实体中的文本声明时出错。
- xml.parsers.expat.XML_ERROR_PUBLICID
- 在公共 ID 中发现不允许的字符。
- xml.parsers.expat.XML_ERROR_SUSPENDED
- 请求的操作是在暂停的解析器上进行的,但不被允许。 这包括尝试提供额外的输入或停止解析器。
- xml.parsers.expat.XML_ERROR_NOT_SUSPENDED
- 当解析器没有被挂起时,尝试恢复解析器。
- xml.parsers.expat.XML_ERROR_ABORTED
- 这不应报告给 Python 应用程序。
- xml.parsers.expat.XML_ERROR_FINISHED
- 请求的操作是在已完成解析输入的解析器上进行的,但不被允许。 这包括尝试提供额外的输入或停止解析器。
- xml.parsers.expat.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。