53、Python 网络数据处理与编码

Python 网络数据处理与编码

1. quopri 模块

1.1 MIME 类型识别

在处理 MIME 类型时,如果处于默认设置,只有在 IANA 注册的官方 MIME 类型才会被识别,可参考 IANA 媒体类型分配 。若不处于默认设置,一些常见但非官方的 MIME 类型也会被识别。以下是相关函数:
- guess_extension(type [, strict]) :根据文件的 MIME 类型猜测标准文件扩展名,返回包含前导点(.)的文件名扩展名的字符串。对于未知类型返回 None。若 strict 为 True(默认值),则只识别官方 MIME 类型。
- guess_all_extensions(type [, strict]) :与 guess_extension() 类似,但返回所有可能的文件名扩展名列表。
- init([files]) :初始化模块。 files 是一个文件名序列,用于读取以提取类型信息。这些文件包含将 MIME 类型映射到可接受的文件后缀列表的行,例如:

image/jpeg:   jpe jpeg jpg
text/html:    htm html
  • read_mime_types(filename) :从给定的文件名加载类型映射,返回将文件名扩展名映射到 MIME 类型字符串的字典。若文件名不存在或无法读取,则返回 None。
  • add_type(type, ext [, strict]) :向映射中添加新的 MIME 类型。 type 是 MIME 类型,如 ‘text/plain’; ext 是文件名扩展名,如 ‘.txt’; strict 是一个布尔值,指示该类型是否为官方注册的 MIME 类型,默认值为 True。

1.2 引用可打印编码与解码

quopri 模块用于对字节字符串进行引用可打印传输编码和解码。这种格式主要用于编码 8 位文本文件,这些文件大多可以作为 ASCII 读取,但可能包含少量不可打印或特殊字符(例如,控制字符或范围在 128 - 255 的非 ASCII 字符)。引用可打印编码的规则如下:
- 任何可打印的非空白 ASCII 字符(除了 ‘=’)按原样表示。
- ‘=’ 字符用作转义字符。后跟两个十六进制数字时,它表示具有该值的字符(例如,’=0C’)。等号由 ‘=3D’ 表示。如果 ‘=’ 出现在行尾,则表示软换行。只有当输入文本的长行必须拆分为多个输出行时才会出现这种情况。
- 空格和制表符按原样保留,但不能出现在行尾。

例如,若文档包含文本 “Copyright © 2009”,Python 字节字符串表示为 b'Copyright \xa9 2009' ,其引用可打印版本为 b'Copyright =A9 2009' ,其中特殊字符 '\xa9' 被转义序列 ‘=A9’ 替换。

以下是相关的编码和解码函数:
- decode(input, output [, header]) :将字节解码为引用可打印格式。 input output 是二进制模式打开的文件对象。若 header 为 True,则下划线(_)将被解释为空格,否则保持不变,这用于解码已编码的 MIME 头,默认 header 为 False。
- decodestring(s [, header]) :解码字符串 s s 可以是 Unicode 或字节字符串,但结果始终是字节字符串。 header 的含义与 decode() 相同。
- encode(input, output, quotetabs [, header]) :将字节编码为引用可打印格式。 input output 是二进制模式打开的文件对象。若 quotetabs 为 True,则除了正常的引用规则外,还会强制对制表符进行引用,否则制表符按原样保留,默认 quotetabs 为 False。 header 的含义与 decode() 相同。
- encodestring(s [, quotetabs [, header]]) :编码字节字符串 s ,结果也是字节字符串。 quotetabs header 的含义与 encode() 相同。

注意 :引用可打印数据编码早于 Unicode,仅适用于 8 位数据。尽管它最常用于文本,但实际上只适用于以单字节表示的 ASCII 和扩展 ASCII 字符。使用此模块时,请确保所有文件都处于二进制模式,并且处理的是字节字符串。

2. XML 处理

2.1 XML 概述

Python 包含多种用于处理 XML 数据的模块。XML 处理是一个广泛的主题,本文假设读者已经熟悉一些基本的 XML 概念。Python 提供了两种 XML 支持:一是对两种行业标准的 XML 解析方法(SAX 和 DOM)的基本支持;二是 ElementTree 接口,这是 Python 特有的 XML 解析方法,利用了 Python 语言特性,大多数用户认为它比 SAX 或 DOM 更简单、更快速。

2.2 XML 示例文档

以下是一个典型的 XML 文档示例,描述了一个食谱:

<?xml version="1.0" encoding="iso-8859-1"?>
<recipe>
    <title>
        Famous Guacamole
    </title>
    <description>
        A southwest favorite!
    </description>
    <ingredients>
        <item num="4"> Large avocados, chopped </item>
        <item num="1"> Tomato, chopped </item>
        <item num="1/2" units="C"> White onion, chopped </item>
        <item num="2" units="tbl"> Fresh squeezed lemon juice </item>
        <item num="1"> Jalapeno pepper, diced </item>
        <item num="1" units="tbl"> Fresh cilantro, minced </item>
        <item num="1" units="tbl"> Garlic, minced </item>
        <item num="3" units="tsp"> Salt </item>
        <item num="12" units="bottles"> Ice-cold beer </item>
    </ingredients>
    <directions>
        Combine all ingredients and hand whisk to desired consistency.
        Serve and enjoy with ice-cold beers.
    </directions>
</recipe>

该文档由以标签开始和结束的元素组成,元素通常嵌套并组织成层次结构。每个文档有一个根元素,在此示例中为 <recipe> 元素。元素可以有属性,如 <item> 元素的 num 属性。

2.3 xml.dom.minidom 模块

xml.dom.minidom 模块根据 DOM 规范提供了基本的 XML 文档解析支持,并将其存储在内存中作为树结构。

2.3.1 解析函数
  • parse(file [, parser]) :解析文件内容并返回表示文档树顶部的节点。 file 可以是文件名或已打开的文件对象。 parser 是可选的 SAX2 兼容解析器对象,用于构建树,若省略则使用默认解析器。
  • parseString(string [, parser]) :与 parse() 相同,但输入数据以字符串形式提供。
2.3.2 节点属性和方法

解析函数返回的文档树由一组链接在一起的节点组成。每个节点 n 具有以下属性和方法:

节点属性 描述
n.attributes 包含属性值的映射对象(如果有)
n.childNodes n 的所有子节点列表
n.firstChild 节点 n 的第一个子节点
n.lastChild 节点 n 的最后一个子节点
n.localName 元素的本地标签名。如果标签中出现冒号(例如, <foo:bar ...> ),则只包含冒号后面的部分
n.namespaceURI n 关联的命名空间(如果有)
n.nextSibling 树中 n 之后且具有相同父节点的节点。如果 n 是最后一个兄弟节点,则为 None
n.nodeName 节点的名称,含义取决于节点类型
n.nodeType 描述节点类型的整数,设置为 Node 类的类变量之一,如 ATTRIBUTE_NODE CDATA_SECTION_NODE
n.nodeValue 节点的值,含义取决于节点类型
n.parentNode 对父节点的引用
n.prefix 标签名中冒号之前的部分。例如,元素 <foo:bar ...> 的前缀为 ‘foo’
n.previousSibling 树中 n 之前且具有相同父节点的节点

节点的方法通常用于操作树结构,例如:
- n.appendChild(child) :向 n 添加新的子节点 child ,新子节点添加到其他子节点的末尾。
- n.cloneNode(deep) :复制节点 n 。若 deep 为 True,则所有子节点也会被克隆。
- n.hasAttributes() :若节点有任何属性,则返回 True。
- n.hasChildNodes() :若节点有任何子节点,则返回 True。
- n.insertBefore(newchild, ichild) :在另一个子节点 ichild 之前插入新子节点 newchild ichild 必须已经是 n 的子节点。
- n.isSameNode(other) :若节点 other 引用与 n 相同的 DOM 节点,则返回 True。
- n.normalize() :将相邻的文本节点合并为一个文本节点。
- n.removeChild(child) :从 n 中移除子节点 child
- n.replaceChild(newchild,oldchild) :用 newchild 替换子节点 oldchild oldchild 必须已经是 n 的子节点。

2.3.3 常见节点类型

常见的节点类型有 Document、Element 和 Text 节点:
- Document 节点 :文档节点 d 出现在整个文档树的顶部,代表整个文档。它具有以下方法和属性:
- d.documentElement :包含整个文档的根元素。
- d.getElementsByTagName(tagname) :搜索所有子节点并返回具有给定标签名 tagname 的元素列表。
- d.getElementsByTagNameNS(namespaceuri, localname) :搜索所有子节点并返回具有给定命名空间 URI 和本地名称的元素列表,返回的列表是 NodeList 类型的对象。
- Element 节点 :元素节点 e 代表单个 XML 元素,如 <foo>...</foo> 。要获取元素的文本,需要查找作为子节点的 Text 节点。元素节点具有以下属性和方法:
- e.tagName :元素的标签名。例如,若元素定义为 <foo ...> ,标签名是 ‘foo’。
- e.getElementsByTagName(tagname) :返回具有给定标签名的所有子元素列表。
- e.getElementsByTagNameNS(namespaceuri, localname) :返回具有给定命名空间中给定标签名的所有子元素列表。
- e.hasAttribute(name) :若元素具有名为 name 的属性,则返回 True。
- e.hasAttributeNS(namespaceuri, localname) :若元素具有由 namespaceuri localname 命名的属性,则返回 True。
- e.getAttribute(name) :返回属性 name 的值,若属性不存在则返回空字符串。
- e.getAttributeNS(namespaceuri, localname) :返回由 namespaceuri localname 命名的属性的值,若属性不存在则返回空字符串。
- Text 节点 :文本节点用于表示文本数据,文本数据存储在 Text 对象 t t.data 属性中。与给定文档元素关联的文本总是存储在该元素的子 Text 节点中。

2.3.4 实用函数

节点上定义了以下实用方法,这些方法不是 DOM 标准的一部分,但由 Python 提供以方便使用和调试:
- n.toprettyxml([indent [, newl]]) :创建一个格式良好的字符串,包含节点 n 及其子节点表示的 XML。 indent 指定缩进字符串,默认为制表符( \t ); newl 指定换行符,默认为 \n
- n.toxml([encoding]) :创建一个包含节点 n 及其子节点表示的 XML 的字符串。 encoding 指定编码(例如,’utf-8’),若未提供编码,则输出文本中不指定编码。
- n.writexml(writer [, indent [, addindent [, newl]]]) :将 XML 写入 writer writer 可以是任何提供与文件接口兼容的 write() 方法的对象。 indent 指定 n 的缩进, addindent 指定应用于 n 的子节点的增量缩进, newl 指定换行符。

2.3.5 DOM 示例

以下示例展示了如何使用 xml.dom.minidom 模块解析 XML 文件并提取信息:

from xml.dom import minidom

doc = minidom.parse("recipe.xml")
ingredients = doc.getElementsByTagName("ingredients")[0]
items = ingredients.getElementsByTagName("item")
for item in items:
    num = item.getAttribute("num")
    units = item.getAttribute("units")
    text = item.firstChild.data.strip()
    quantity = "%s %s" % (num, units)
    print("%-10s %s" % (quantity, text))

2.4 xml.etree.ElementTree 模块

xml.etree.ElementTree 模块定义了一个灵活的容器对象 ElementTree ,用于存储和操作层次结构数据。虽然该对象通常与 XML 处理结合使用,但实际上它是通用的,类似于列表和字典的结合。

2.4.1 ElementTree 对象

ElementTree([element [, file]]) :创建一个新的 ElementTree 对象。 element 是表示树的根节点的实例,支持后续描述的元素接口。 file 可以是文件名或文件类对象,用于读取 XML 数据以填充树。

2.4.2 ElementTree 方法

ElementTree 实例 tree 具有以下方法:
- tree._setroot(element) :将根元素设置为 element
- tree.find(path) :查找并返回树中第一个类型与给定路径匹配的顶级元素。 path 是一个字符串,描述元素类型及其相对于其他元素的位置。路径语法如下:

路径 描述
'tag' 仅匹配具有给定标签的顶级元素,例如 <tag>...</tag> ,不匹配较低级别定义的元素
'parent/tag' 若元素的标签为 ‘tag’ 且是标签为 ‘parent’ 的元素的子元素,则匹配该元素
'*' 选择所有子元素。例如, '*/tag' 将匹配所有标签名为 ‘tag’ 的孙元素
'.' 从当前节点开始搜索
'//' 选择元素下所有级别的所有子元素。例如, './/tag' 匹配所有标签为 ‘tag’ 的元素

如果处理涉及 XML 命名空间的文档,路径中的标签字符串应具有 '{uri}tag' 的形式,其中 uri 是字符串,如 'http://www.w3.org/TR/html4/'
- tree.findall(path) :查找树中所有与给定路径匹配的顶级元素,并按文档顺序返回它们作为列表或迭代器。
- tree.findtext(path [, default]) :返回树中第一个与给定路径匹配的顶级元素的元素文本。若未找到匹配元素,则返回 default 字符串。
- tree.getiterator([tag]) :创建一个迭代器,按顺序生成树中所有标签与 tag 匹配的元素。若省略 tag ,则按顺序返回树中的每个元素。
- tree.getroot() :返回树的根元素。
- tree.parse(source [, parser]) :解析外部 XML 数据并将根元素替换为结果。 source 可以是文件名或表示 XML 数据的文件类对象。 parser 是可选的 TreeBuilder 实例。
- tree.write(file [, encoding]) :将树的全部内容写入文件。 file 可以是文件名或用于写入的文件类对象。 encoding 是输出编码,若未指定则默认为解释器的默认编码(大多数情况下为 ‘utf-8’ 或 ‘ascii’)。

2.4.3 创建元素

可以使用以下函数创建元素:
- Comment([text]) :创建一个新的注释元素, text 是包含元素文本的字符串或字节字符串。在解析或写入输出时,该元素映射到 XML 注释。
- Element(tag [, attrib [, **extra]]) :创建一个新元素, tag 是元素名称。 attrib 是元素属性的字典,以字符串或字节字符串指定。任何额外的关键字参数也用于设置元素属性。
- fromstring(text) :从 text 中的 XML 文本片段创建元素,与 XML() 相同。
- ProcessingInstruction(target [, text]) :创建一个对应于处理指令的新元素。 target text 都是字符串或字节字符串。在映射到 XML 时,该元素对应于 '<?target text?>'
- SubElement(parent, tag [, attrib [, **extra]]) :与 Element() 相同,但会自动将新元素添加为 parent 元素的子元素。
- XML(text) :通过解析 text 中的 XML 代码片段创建元素。例如,若 text 设置为 <foo>....</foo> ,将创建一个标签为 ‘foo’ 的标准元素。
- XMLID(text) :与 XML(text) 相同,除了收集 ‘id’ 属性并用于构建将 ID 值映射到元素的字典。返回一个元组 (elem, idmap) ,其中 elem 是新元素, idmap 是 ID 映射字典。

2.4.4 元素接口

ElementTree 中存储的元素虽然类型可能不同,但都支持一个公共接口。如果 elem 是任何元素,则定义了以下 Python 运算符:

运算符 描述
elem[n] 返回 elem 的第 n 个子元素
elem[n] = newelem elem 的第 n 个子元素更改为不同的元素 newelem
del elem[n] 删除 elem 的第 n 个子元素
len(elem) elem 的子元素数量

所有元素具有以下基本数据属性:

属性 描述
elem.tag 标识元素类型的字符串,例如 <foo>...</foo> 的标签为 ‘foo’
elem.text 与元素关联的数据,通常是包含 XML 元素开始和结束标签之间文本的字符串
elem.tail 与属性一起存储的额外数据,对于 XML,通常是元素结束标签之后但下一个标签开始之前的空白字符串
elem.attrib 包含元素属性的字典

元素支持以下方法,其中一些方法类似于字典的方法:
- elem.append(subelement) :将子元素 subelement 添加到子元素列表中。
- elem.clear() :清除元素中的所有数据,包括属性、文本和子元素。
- elem.find(path) :查找第一个类型与 path 匹配的子元素。
- elem.findall(path) :查找所有类型与 path 匹配的子元素,按文档顺序返回匹配元素的列表或可迭代对象。
- elem.findtext(path [, default]) :查找第一个类型与 path 匹配的元素的文本。若没有匹配项,则返回 default 字符串。
- elem.get(key [, default]) :获取属性 key 的值。若属性不存在,则返回 default 默认值。如果涉及 XML 命名空间,则 key 应为 '{uri}key}' 的形式。
- elem.getchildren() :按文档顺序返回所有子元素。
- elem.getiterator([tag]) :返回一个迭代器,生成所有类型与 tag 匹配的子元素。
- elem.insert(index, subelement) :在子元素列表的位置 index 插入子元素。
- elem.items() :返回所有元素属性作为 (name, value) 对的列表。
- elem.keys() :返回所有属性名称的列表。
- elem.remove(subelement) :从子元素列表中移除元素 subelement
- elem.set(key, value) :将属性 key 设置为值 value

2.4.5 树构建

TreeBuilder([element_factory]) :用于通过一系列 start() end() data() 调用构建 ElementTree 结构,这些调用在解析文件或遍历其他树结构时会被触发。 element_factory 是用于创建新元素实例的操作函数。

TreeBuilder 实例 t 具有以下方法:
- t.close() :关闭树构建器并返回已创建的顶级 ElementTree 对象。
- t.data(data) :将文本数据添加到当前正在处理的元素中。
- t.end(tag) :关闭当前正在处理的元素并返回最终的元素对象。
- t.start(tag, attrs) :创建一个新元素, tag 是元素名称, attrs 是包含属性值的字典。

2.4.6 实用函数

定义了以下实用函数:
- dump(elem) :将元素结构 elem 转储到 sys.stdout 进行调试,输出通常是 XML。
- iselement(elem) :检查 elem 是否为有效的元素对象。
- iterparse(source [, events]) :从 source 逐步解析 XML。 source 可以是文件名或引用 XML 数据的文件类对象。 events 是要生成的事件类型列表,可能的事件类型有 'start' 'end' 'start-ns' 'end-ns' 。若省略,则仅生成 'end' 事件。该函数返回一个迭代器,生成元组 (event, elem) ,其中 event 是字符串,如 'start' 'end' elem 是正在处理的元素。
- parse(source) :将 XML 源完全解析为 ElementTree 对象, source 是包含 XML 数据的文件名或文件类对象。
- tostring(elem) :创建一个表示 elem 及其所有子元素的 XML 字符串。

2.4.7 XML 示例

以下是使用 ElementTree 解析示例食谱文件并打印配料列表的示例:

from xml.etree.ElementTree import ElementTree

doc = ElementTree(file="recipe.xml")
ingredients = doc.find('ingredients')
for item in ingredients.findall('item'):
    num = item.get('num')
    units = item.get('units', '')
    text = item.text.strip()
    quantity = "%s %s" % (num, units)
    print("%-10s %s" % (quantity, text))

也可以使用路径语法简化操作:

from xml.etree.ElementTree import ElementTree

doc = ElementTree(file="recipe.xml")
for item in doc.findall(".//item"):
    num = item.get('num')
    units = item.get('units', '')
    text = item.text.strip()
    quantity = "%s %s" % (num, units)
    print("%-10s %s" % (quantity, text))

对于使用命名空间的 XML 文件,例如 recipens.xml

<?xml version="1.0" encoding="iso-8859-1"?>
<recipe xmlns:r="http://www.dabeaz.com/namespaces/recipe">
    <r:title>
        Famous Guacamole
    </r:title>
    <r:description>
        A southwest favorite!
    </r:description>
    <r:ingredients>
        <r:item num="4"> Large avocados, chopped </r:item>
        <!-- 其他配料 -->
    </r:ingredients>
    <r:directions>
        Combine all ingredients and hand whisk to desired consistency.
        Serve and enjoy with ice-cold beers.
    </r:directions>
</recipe>

可以使用字典映射命名空间前缀到关联的命名空间 URI:

from xml.etree.ElementTree import ElementTree

doc = ElementTree(file="recipens.xml")
ns = {
    'r': 'http://www.dabeaz.com/namespaces/recipe'
}
ingredients = doc.find('{%(r)s}ingredients' % ns)
for item in ingredients.findall('{%(r)s}item' % ns):
    num = item.get('num')
    units = item.get('units', '')
    text = item.text.strip()
    quantity = "%s %s" % (num, units)
    print("%-10s %s" % (quantity, text))

对于大型 XML 文件,使用 ElementTree.iterparse() 函数可以减少内存消耗。例如,处理包含大量专辑信息的 music.xml 文件:

from xml.etree.ElementTree import iterparse

iparse = iterparse("music.xml", ['start', 'end'])
# 找到顶级音乐元素
for event, elem in iparse:
    if event == 'start' and elem.tag == 'music':
        musicNode = elem
        break
# 获取所有专辑
albums = (elem for event, elem in iparse if event == 'end' and elem.tag == 'album')
for album in albums:
    # 进行一些处理
    ...
    musicNode.remove(album)  # 处理完后丢弃专辑

使用 iterparse() 时,关键是及时丢弃不再使用的数据,以保持低内存占用。

2.5 xml.sax 模块

xml.sax 模块使用 SAX2 API 提供 XML 文档解析支持。

2.5.1 解析函数
  • parse(file, handler [, error_handler]) :解析 XML 文档 file file 可以是文件名或打开的文件对象。 handler 是内容处理程序对象, error_handler 是可选的 SAX 错误处理程序对象,更多信息可在在线文档中找到。
  • parseString(string, handler [, error_handler]) :与 parse() 相同,但解析字符串中包含的 XML 数据。
2.5.2 处理程序对象

要进行任何处理,需要向 parse() parseString() 函数提供内容处理程序对象。定义处理程序时,需要定义一个继承自 ContentHandler 的类。 ContentHandler 实例 c 具有以下方法,可根据需要在处理程序类中重写:
- c.characters(content) :解析器调用此方法提供原始字符数据, content 是包含字符的字符串。
- c.endDocument() :解析器到达文档末尾时调用。
- c.endElement(name) :到达元素 name 的末尾时调用。
- c.endElementNS(name, qname) :到达涉及 XML 命名空间的元素末尾时调用。
- c.endPrefixMapping(prefix) :到达 XML 命名空间末尾时调用。
- c.ignorableWhitespace(whitespace) :在文档中遇到可忽略的空白时调用。
- c.processingInstruction(target, data) :遇到 XML 处理指令时调用。
- c.setDocumentLocator(locator) :解析器调用此方法提供定位器对象,用于跟踪行号、列号等信息。
- c.skippedEntity(name) :解析器跳过实体时调用。
- c.startDocument() :文档开始时调用。
- c.startElement(name, attrs) :遇到新的 XML 元素时调用, attrs 对象提供了获取属性信息的方法,如 attrs.getLength() attrs.getNames() 等。
- c.startElementNS(name, qname, attrs) :遇到使用 XML 命名空间的新 XML 元素时调用, attrs 对象除了具有 startElement() 中的方法外,还添加了处理命名空间的方法。
- c.startPrefixMapping(prefix, uri) :在 XML 命名空间声明开始时调用。

2.5.3 示例

以下示例展示了一个基于 SAX 的解析器,用于打印食谱文件中的配料列表:

from xml.sax import ContentHandler, parse

class RecipeHandler(ContentHandler):
    def startDocument(self):
        self.initem = False

    def startElement(self, name, attrs):
        if name == 'item':
            self.num = attrs.get('num', '1')
            self.units = attrs.get('units', 'none')
            self.text = []
            self.initem = True

    def endElement(self, name):
        if name == 'item':
            text = "".join(self.text)
            if self.units == 'none':
                self.units = ""
            unitstr = "%s %s" % (self.num, self.units)
            print("%-10s %s" % (unitstr, text.strip()))
            self.initem = False

    def characters(self, data):
        if self.initem:
            self.text.append(data)

parse("recipe.xml", RecipeHandler())

2.6 xml.sax.saxutils 模块

xml.sax.saxutils 模块定义了一些实用函数和对象,常用于 SAX 解析器,也在其他地方普遍有用。
- escape(data [, entities]) :给定字符串 data ,此函数将某些字符替换为转义序列。 entities 是可选的字典,用于将字符映射到转义序列。
- unescape(data [, entities]) :对 data 中出现的特殊转义序列进行反转义。 entities 是可选的字典,将实体映射到未转义的字符值。
- quoteattr(data [, entities]) :对字符串 data 进行转义,并进行额外处理,使结果值可作为 XML 属性值使用。
- XMLGenerator([out [, encoding]]) :一个 ContentHandler 对象,将解析的 XML 数据作为 XML 文档回显到输出流,重新创建原始 XML 文档。 out 是输出文档,默认为 sys.stdout encoding 是字符编码,默认为 ‘iso-8859-1’,可用于调试解析代码。

3. 总结与对比

3.1 各模块特点总结

模块名称 特点 适用场景
quopri 用于对字节字符串进行引用可打印传输编码和解码,适用于处理包含少量非打印或特殊字符的 8 位文本文件 处理包含特殊字符的邮件内容、网页数据等
xml.dom.minidom 根据 DOM 规范将 XML 文档存储为树结构,提供了丰富的节点属性和方法来操作树结构 需要对 XML 文档进行复杂操作,如修改节点、添加属性等
xml.etree.ElementTree 定义了灵活的 ElementTree 对象,使用方便,路径语法简化了元素查找操作,内存占用相对较小 处理简单的 XML 文档,快速提取信息
xml.sax 使用 SAX2 API 解析 XML 文档,基于事件处理,逐行解析,内存占用低 处理大型 XML 文档,只需要顺序读取和处理数据
xml.sax.saxutils 提供了一些实用函数和对象,如转义、反转义字符,用于辅助 SAX 解析器 处理 XML 数据时需要对字符进行转义或反转义操作

3.2 不同 XML 解析方法对比

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px

    A([开始]):::startend --> B{选择解析方法}:::process
    B --> C[DOM]:::process
    B --> D[ElementTree]:::process
    B --> E[SAX]:::process
    C --> C1(构建树结构):::process
    C --> C2(支持复杂操作):::process
    C --> C3(内存占用高):::process
    D --> D1(灵活容器对象):::process
    D --> D2(路径语法方便):::process
    D --> D3(内存占用适中):::process
    E --> E1(基于事件处理):::process
    E --> E2(逐行解析):::process
    E --> E3(内存占用低):::process
    C1 --> C4(适用于复杂修改):::process
    D2 --> D4(适用于简单提取):::process
    E2 --> E4(适用于大型文件):::process
    C4 --> F([结束]):::startend
    D4 --> F
    E4 --> F

从上述流程图可以看出,不同的 XML 解析方法有不同的特点和适用场景。DOM 方法适合对 XML 文档进行复杂的修改和操作,但内存占用高;ElementTree 方法使用方便,路径语法简化了元素查找,适用于简单的 XML 文档信息提取;SAX 方法基于事件处理,逐行解析,内存占用低,适合处理大型 XML 文件。

3.3 实际应用建议

  • 小型 XML 文件 :如果 XML 文件较小,且需要对文档进行一些修改和操作,建议使用 xml.dom.minidom 模块。例如,需要修改 XML 文档中的节点属性、添加或删除节点等操作时,DOM 方法可以方便地实现这些功能。
  • 简单 XML 信息提取 :对于简单的 XML 文档,只需要提取其中的信息, xml.etree.ElementTree 模块是一个不错的选择。其路径语法可以快速定位到需要的元素,代码实现简单易懂。
  • 大型 XML 文件 :当处理大型 XML 文件时,为了避免内存占用过高,建议使用 xml.sax 模块。SAX 方法基于事件处理,逐行解析 XML 文档,只在需要时处理数据,不会将整个文档加载到内存中。

4. 注意事项

4.1 quopri 模块注意事项

  • 引用可打印数据编码早于 Unicode,仅适用于 8 位数据。在使用 quopri 模块时,要确保所有文件都处于二进制模式,并且处理的是字节字符串。
  • 处理包含特殊字符的文本时,要注意字符编码的问题,避免出现乱码。

4.2 XML 处理注意事项

  • 命名空间处理 :在处理包含命名空间的 XML 文档时,要注意路径语法和属性查找方法的变化。例如,在 ElementTree 模块中,路径中的标签字符串需要使用 '{uri}tag' 的形式;在 xml.dom.minidom xml.sax 模块中,需要使用相应的命名空间方法来查找元素和属性。
  • 内存管理 :处理大型 XML 文件时,要选择合适的解析方法,避免内存占用过高。如使用 xml.sax 模块或 ElementTree.iterparse() 函数,及时丢弃不再使用的数据。

5. 拓展学习

5.1 第三方库推荐

  • lxml.etree :提供了 ElementTree API 到流行的 libxml2 libxslt 库的接口,支持 XPATH、XSLT 等功能,功能强大,性能优越。可以用于处理复杂的 XML 文档,进行 XML 转换和验证等操作。
  • BeautifulSoup :虽然主要用于 HTML 解析,但也可以处理 XML 数据。它提供了简单易用的 API,能够快速定位和提取 XML 文档中的信息。

5.2 学习资源

  • 官方文档 :Python 官方文档是学习这些模块的最佳资源,详细介绍了各个模块的功能和使用方法。
  • 在线教程 :网上有很多关于 Python 网络数据处理和 XML 处理的教程,可以帮助你深入理解和掌握这些知识。
  • 开源项目 :可以参考一些开源项目,学习他人的代码实现和处理思路。

通过学习和掌握这些模块,你可以更加高效地处理 Python 中的网络数据和 XML 文档,为开发各种应用程序打下坚实的基础。在实际应用中,要根据具体需求选择合适的模块和方法,同时注意处理过程中的细节和注意事项,以确保程序的正确性和性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值