Python互联网数据处理与编码:从MIME到XML
1. MIME类型处理
1.1 类型识别与扩展名猜测
在处理文件时,MIME类型的识别至关重要。默认情况下,仅识别在IANA注册的官方MIME类型。以下是一些与此相关的重要函数:
-
guess_extension(type [, strict])
:根据MIME类型猜测文件的标准扩展名,返回包含前导点的字符串,未知类型返回
None
。若
strict
为
True
(默认),则仅识别官方MIME类型。
-
guess_all_extensions(type [, strict])
:与
guess_extension()
类似,但返回所有可能的文件名扩展名列表。
-
init([files])
:初始化模块,
files
是用于提取类型信息的文件名序列,文件中包含将MIME类型映射到可接受文件后缀的行。
-
read_mime_types(filename)
:从给定文件名加载类型映射,返回将文件名扩展名映射到MIME类型字符串的字典,若文件不存在或无法读取则返回
None
。
-
add_type(type, ext [, strict])
:向映射中添加新的MIME类型,
type
是MIME类型,
ext
是文件名扩展名,
strict
表示该类型是否为官方注册的MIME类型,默认
strict
为
True
。
1.2 示例
以下是一个简单的示例,展示如何使用这些函数:
import mimetypes
# 初始化模块
mimetypes.init()
# 猜测扩展名
extension = mimetypes.guess_extension('image/jpeg')
print(extension)
# 添加新的MIME类型
mimetypes.add_type('text/markdown', '.md')
2. Quopri模块:引用可打印编码与解码
2.1 编码规则
Quopri模块用于对字节字符串进行引用可打印的传输编码和解码。这种格式主要用于编码大部分可作为ASCII读取,但可能包含少量非打印或特殊字符(如控制字符或范围在128 - 255的非ASCII字符)的8位文本文件。编码规则如下:
- 任何可打印的非空白ASCII字符(除
=
外)按原样表示。
-
=
字符用作转义字符,后跟两个十六进制数字时,表示具有该值的字符(如
=0C
),等号本身表示为
=3D
。若
=
出现在行尾,则表示软换行,仅在输入文本的长行必须拆分为多个输出行时出现。
- 空格和制表符按原样保留,但不能出现在行尾。
2.2 函数介绍
-
decode(input, output [, header]):将字节解码为Quopri格式,input和output是二进制模式打开的文件对象。若header为True,则下划线(_)将被解释为空格,默认header为False。 -
decodestring(s [, header]):解码字符串s,结果始终为字节字符串,header含义与decode()相同。 -
encode(input, output, quotetabs [, header]):将字节编码为Quopri格式,quotetabs若设置为True,则强制对制表符进行引用,默认quotetabs为False,header含义与decode()相同。 -
encodestring(s [, quotetabs [, header]]):编码字节字符串s,结果也是字节字符串,quotetabs和header含义与encode()相同。
2.3 示例
import quopri
# 编码字符串
encoded = quopri.encodestring(b'Copyright \xa9 2009')
print(encoded)
# 解码字符串
decoded = quopri.decodestring(encoded)
print(decoded)
3. XML处理
3.1 XML概述
Python提供了多种处理XML数据的模块,涵盖了SAX、DOM和ElementTree等解析方法。SAX基于事件处理,按顺序读取XML文档,遇到XML元素时触发处理函数;DOM构建表示整个XML文档的树结构,构建完成后提供遍历树和提取数据的接口;ElementTree是Python特有的XML解析方法,充分利用Python语言特性,对大多数用户来说比SAX或DOM更简单、更快速。
3.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>
3.3 xml.dom.minidom模块
3.3.1 解析函数
-
parse(file [, parser]):解析文件内容,返回表示文档树顶部的节点,file可以是文件名或已打开的文件对象,parser是可选的SAX2兼容解析器对象,若省略则使用默认解析器。 -
parseString(string [, parser]):与parse()相同,但输入数据以字符串形式提供。
3.3.2 节点属性和方法
文档树由一系列节点组成,每个节点
n
具有以下属性和方法:
|属性/方法|描述|
|----|----|
|
n.attributes
|保存属性值的映射对象(如果有)|
|
n.childNodes
|
n
的所有子节点列表|
|
n.firstChild
|节点
n
的第一个子节点|
|
n.lastChild
|节点
n
的最后一个子节点|
|
n.localName
|元素的本地标签名|
|
n.namespaceURI
|与
n
关联的命名空间(如果有)|
|
n.nextSibling
|树中
n
之后且具有相同父节点的节点,若
n
是最后一个兄弟节点则为
None
|
|
n.nodeName
|节点的名称,含义取决于节点类型|
|
n.nodeType
|描述节点类型的整数|
|
n.nodeValue
|节点的值,含义取决于节点类型|
|
n.parentNode
|对父节点的引用|
|
n.prefix
|标签名中冒号之前的部分|
|
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
|
|
n.isSameNode(other)
|若节点
other
引用与
n
相同的DOM节点则返回
True
|
|
n.normalize()
|将相邻的文本节点合并为一个文本节点|
|
n.removeChild(child)
|从
n
中移除子节点
child
|
|
n.replaceChild(newchild,oldchild)
|用
newchild
替换子节点
oldchild
|
3.3.3 示例
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))
3.4 xml.etree.ElementTree模块
3.4.1 ElementTree对象
ElementTree([element [, file]])
:创建新的
ElementTree
对象,
element
表示树的根节点,
file
可以是文件名或文件类对象,用于读取XML数据以填充树。
3.4.2 方法和路径语法
ElementTree
对象具有以下方法:
-
tree._setroot(element)
:将根元素设置为
element
。
-
tree.find(path)
:查找并返回树中第一个类型与给定路径匹配的顶级元素,路径语法如下表所示:
|路径|描述|
|----|----|
|
'tag'
|仅匹配具有给定标签的顶级元素|
|
'parent/tag'
|若元素具有标签
tag
且是具有标签
parent
的元素的子元素,则匹配|
|
'*'
|选择所有子元素|
|
'.'
|从当前节点开始搜索|
|
'//'
|选择元素下方所有级别的子元素|
-
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
是输出编码,默认使用解释器的默认编码。
3.4.3 创建元素
可以使用以下函数创建元素:
-
Comment([text])
:创建新的注释元素。
-
Element(tag [, attrib [, **extra]])
:创建新元素,
tag
是元素名称,
attrib
是元素属性的字典,
extra
中的任何额外关键字参数也用于设置元素属性。
-
fromstring(text)
:从XML文本片段创建元素。
-
ProcessingInstruction(target [, text])
:创建对应于处理指令的新元素。
-
SubElement(parent, tag [, attrib [, **extra]])
:与
Element()
相同,但自动将新元素添加为
parent
元素的子元素。
-
XML(text)
:通过解析XML代码片段创建元素。
-
XMLID(text)
:与
XML(text)
相同,但收集
'id'
属性并用于构建将ID值映射到元素的字典,返回元组
(elem, idmap)
。
3.4.4 元素接口
元素支持以下Python运算符和方法:
|运算符/方法|描述|
|----|----|
|
elem[n]
|返回
elem
的第
n
个子元素|
|
elem[n] = newelem
|将
elem
的第
n
个子元素更改为不同的元素
newelem
|
|
del elem[n]
|删除
elem
的第
n
个子元素|
|
len(elem)
|
elem
的子元素数量|
|
elem.tag
|标识元素类型的字符串|
|
elem.text
|与元素关联的数据|
|
elem.tail
|与属性一起存储的额外数据|
|
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
|
|
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
|
3.4.5 示例
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))
3.5 xml.sax模块
3.5.1 解析函数
-
parse(file, handler [, error_handler]):解析XML文档,file可以是文件名或打开的文件对象,handler是内容处理程序对象,error_handler是可选的SAX错误处理程序对象。 -
parseString(string, handler [, error_handler]):与parse()相同,但解析字符串中包含的XML数据。
3.5.2 处理程序对象
要执行任何处理,需要向
parse()
或
parseString()
函数提供内容处理程序对象。可以定义一个继承自
ContentHandler
的类,重写以下方法:
|方法|描述|
|----|----|
|
c.characters(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元素时调用|
|
c.startElementNS(name, qname, attrs)
|遇到新的XML元素且使用XML命名空间时调用|
|
c.startPrefixMapping(prefix, uri)
|开始XML命名空间声明时调用|
3.5.3 示例
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())
3.6 xml.sax.saxutils模块
该模块定义了一些常用的实用函数和对象:
-
escape(data [, entities])
:将字符串中的某些字符替换为转义序列。
-
unescape(data [, entities])
:对数据中出现的特殊转义序列进行反转义。
-
quoteattr(data [, entities])
:对字符串进行转义,并进行额外处理,使结果值可作为XML属性值。
-
XMLGenerator([out [, encoding]])
:一个
ContentHandler
对象,将解析的XML数据作为XML文档回显到输出流,可用于调试解析代码。
4. 总结
Python提供了丰富的工具来处理互联网数据,从MIME类型的识别到XML文档的解析,每个模块都有其独特的优势和适用场景。Quopri模块适用于处理包含特殊字符的文本文件的编码和解码,而XML处理模块则提供了多种解析方法,可根据具体需求选择合适的方法。例如,对于简单的XML文档,
ElementTree
模块是最容易和灵活的选择;而对于需要处理复杂XML文档的情况,可能需要使用第三方包。通过合理使用这些工具,可以高效地处理各种互联网数据。
3.7 各XML处理模块对比
为了更清晰地了解不同XML处理模块的特点,下面对
xml.dom.minidom
、
xml.etree.ElementTree
和
xml.sax
进行对比:
| 模块 | 特点 | 适用场景 | 优点 | 缺点 |
| — | — | — | — | — |
|
xml.dom.minidom
| 构建完整的文档树结构,提供丰富的节点操作方法 | 需要频繁遍历和修改文档结构,对文档进行复杂操作 | 提供完整的DOM接口,方便进行节点的增删改查操作 | 占用内存大,解析速度慢,不适合处理大文件 |
|
xml.etree.ElementTree
| 轻量级,操作简单,提供灵活的路径查找语法 | 处理简单的XML文档,需要快速提取数据 | 易于使用,代码简洁,内存占用相对较小 | 缺乏复杂的XML功能,如验证、DTD处理等 |
|
xml.sax
| 基于事件驱动,逐行解析XML文档 | 处理大文件,只需提取部分数据,无需构建完整文档树 | 内存占用小,解析速度快 | 编程复杂度高,需要编写大量的事件处理代码 |
3.8 处理XML命名空间
当XML文档使用命名空间时,处理起来会稍微复杂一些。下面是一个使用
xml.etree.ElementTree
处理命名空间的示例:
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))
在这个示例中,我们使用一个字典
ns
来映射命名空间前缀和URI,然后在路径中使用字符串格式化来填充URI。
3.9 处理大XML文件
对于大XML文件,将整个文件加载到内存中会消耗大量的内存。可以使用
ElementTree.iterparse()
函数来逐行解析文件,避免内存问题。以下是一个示例:
from xml.etree.ElementTree import iterparse
iparse = iterparse("music.xml", ['start', 'end'])
# Find the top-level music element
for event, elem in iparse:
if event == 'start' and elem.tag == 'music':
musicNode = elem
break
# Get all albums
albums = (elem for event, elem in iparse if event == 'end' and elem.tag == 'album')
for album in albums:
# Do some kind of processing
...
musicNode.remove(album) # Throw away the album when done
在这个示例中,我们使用
iterparse()
函数逐行解析文件,当遇到
<music>
元素时,记录该元素。然后,使用生成器表达式获取所有的
<album>
元素,并在处理完每个元素后将其从父元素中移除,以释放内存。
3.10 XML处理流程总结
下面是一个使用mermaid绘制的流程图,展示了处理XML文件的一般流程:
graph LR
A[选择处理模块] --> B{文件大小}
B -->|小文件| C[xml.dom.minidom或xml.etree.ElementTree]
B -->|大文件| D[xml.sax或xml.etree.ElementTree.iterparse]
C --> E{操作复杂度}
E -->|简单操作| F[xml.etree.ElementTree]
E -->|复杂操作| G[xml.dom.minidom]
D --> H[逐行解析数据]
F --> I[提取数据或修改文档]
G --> I
H --> I
I --> J[输出结果]
4. 综合应用示例
假设我们需要从一个包含多个食谱的XML文件中提取所有食谱的名称和所需的啤酒数量,并将结果保存到一个新的文本文件中。以下是一个使用
xml.etree.ElementTree
实现的示例代码:
from xml.etree.ElementTree import ElementTree
# 解析XML文件
doc = ElementTree(file="recipes.xml")
# 打开输出文件
with open("beer_quantity.txt", "w") as outfile:
# 查找所有食谱
recipes = doc.findall(".//recipe")
for recipe in recipes:
# 获取食谱名称
title = recipe.find("title").text.strip()
# 查找啤酒配料
beer_items = recipe.findall(".//item[contains(text(), 'beer')]")
total_beer = 0
for item in beer_items:
num = item.get('num')
try:
total_beer += float(num)
except ValueError:
pass
# 写入结果
outfile.write(f"{title}: {total_beer} bottles of beer\n")
print("结果已保存到beer_quantity.txt")
5. 总结与展望
5.1 总结
Python在互联网数据处理和编码方面提供了丰富的工具和模块。MIME类型处理模块可以帮助我们识别和处理不同类型的文件,Quopri模块适用于处理包含特殊字符的文本文件的编码和解码。在XML处理方面,
xml.dom.minidom
、
xml.etree.ElementTree
和
xml.sax
各有优缺点,可以根据具体需求选择合适的模块。对于简单的XML文档,
ElementTree
模块是最容易和灵活的选择;而对于处理大文件或需要复杂操作的情况,可能需要使用
xml.sax
或
xml.dom.minidom
。
5.2 展望
虽然Python的标准库提供了基本的XML处理功能,但对于一些高级需求,如XML验证、DTD处理、XSLT转换等,可能需要使用第三方库,如
lxml
。未来,随着互联网数据的不断增长和XML技术的不断发展,Python的XML处理能力也将不断完善和扩展。同时,对于其他类型的互联网数据处理,如JSON、CSV等,Python也提供了相应的工具和模块,我们可以进一步探索和应用这些工具,以满足不同的业务需求。
超级会员免费看
632

被折叠的 条评论
为什么被折叠?



