Python—内建模块(8)

97 阅读17分钟

re

re 模块是 Python 中用来处理正则表达式的模块,提供了一系列函数,用于匹配、搜索、替换字符串中的模式

import re

r0 = re.match(r'\d?[A-Z]{3}', '1ASD')
print(r0)
r0 = re.match(r'\d?[A-Z]{3}', 'AD')
print(r0)
"""执行结果:
<re.Match object; span=(0, 4), match='1ASD'>
None
"""

print('王林   王麻子 小林子'.split(' '))
r0 = re.split(r'\s+', '王林   王麻子 小林子,WangLin')
print(r0)
r0 = re.split(r'[\s,]+', '王林   王麻子 小林子,WangLin')
print(r0)
"""执行结果:
['王林', '', '', '王麻子', '小林子']
['王林', '王麻子', '小林子,WangLin']
['王林', '王麻子', '小林子', 'WangLin']
"""

r0 = re.match(r'^(\w{1,10})-(\d{11})$', 'dawn-19999999999')
print(r0)
print(r0.groups())
# group(0) 和整个表达式想匹配
print(r0.group(0), r0.group(1), r0.group(2))
"""执行结果:
<re.Match object; span=(0, 16), match='dawn-19999999999'>
('dawn', '19999999999')
dawn-19999999999 dawn 19999999999
"""

# \d+默认是贪婪匹配;将后面的9全部匹配了
r0 = re.match(r'^(dawn-\d+)(9*)$', 'dawn-19999999999')
print(r0)
print(r0.groups())
"""执行结果:
<re.Match object; span=(0, 16), match='dawn-19999999999'>
('dawn-19999999999', '')
"""


# \d+?变成了非贪婪匹配
r0 = re.match(r'^(dawn-\d+?)(9*)$', 'dawn-19999999999')
print(r0)
print(r0.groups())
"""执行结果:
<re.Match object; span=(0, 16), match='dawn-19999999999'>
('dawn-1', '9999999999')
"""

# re 模块首先编译正则表达式,再用编译后的模式匹配字符串。
# 如果正则表达式重复使用很多次,可以预编译它,避免每次都编译,提高效率。
c0 = re.compile(r'^(\d{3})-(\d{3,8})$')
r0 = c0.match("100-222")
print(r0)
print(r0.groups())
"""执行结果
<re.Match object; span=(0, 7), match='100-222'>
('100', '222')
"""

datetime

datetime 日期时间工具,常用于获取当前时间、格式化时间、进行日期和时间计算、解析和格式化日期字符串等。通过 timedelta,你可以轻松进行日期的加减操作,而 strftimestrptime 则帮助你处理不同格式的时间字符串。

# datetime模块 => 包含一个datetime类
from datetime import datetime, timezone, timedelta

# 获取当前时间
print(datetime.now())
# 获取指定类型的时间
print(datetime(2025, 2, 7, 11, 11, 11))

# 将时间转为时间戳
dt = datetime(2025, 2, 7, 11, 11, 11)
print(dt.timestamp())  # 整数位表示秒

# timestamp转换为datetime
tp = dt.timestamp()
print(datetime.fromtimestamp(tp))  # 本地时间:操作系统设定的时区
print(datetime.fromtimestamp(tp, timezone.utc))  # UTC时间

# datetime转为string《string format time》
print(datetime.strftime(dt, "%Y年%m月%d %H:%M:%S"))
print(datetime.strftime(datetime.now(), '%a, %b %d %H:%M'))

# string转为datetime《string parse time》
dt_obj = datetime.strptime("2025-02-17 12:45:30", "%Y-%m-%d %H:%M:%S")
print(type(dt_obj), dt_obj)

# datetime加减
"""
对日期和时间进行加减实际上就是把datetime往后或往前计算,得到新的datetime。
加减可以直接用+和-运算符,不过需要导入timedelta这个类
"""
now = datetime.now()
print(now + timedelta(hours=1))
print(now - timedelta(days=10, hours=1))

# 创建时区UTC+8:00
tz_utc_8 = timezone(timedelta(hours=8))
print(datetime.now())
# 强行设置为UTC+8:00时区
print(now.replace(tzinfo=tz_utc_8))


# 强制设置时区为UTC+0:00
now = datetime.now().replace(tzinfo=timezone.utc)
print(now)
# 强制转为UTC+8:00
now = now.astimezone(timezone(timedelta(hours=8)))
print(now)

collections

collections 提供了许多有用的集合类

namedtuple

namedtuple() 函数用于创建一个具名元组。它创建一个类似元组的对象,但每个字段都可以通过名称访问。

from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])
point = Point(1, 2)
print(point)

"""
namedtuple可以用属性而不是索引来引用tuple的某个元素。
这样一来,namedtuple可以很方便地定义一种数据类型,它具备tuple的不变性,
又可以根据属性来引用。
可以验证创建的Point对象是tuple的一种子类:
"""
print(isinstance(point, Point)) # True
print(isinstance(point, tuple)) # True

deque

deque 是一个双端队列,支持从两端高效地添加和删除元素。它比列表更适合用作队列或栈。

  • list是线性存储,数据量大的时候,插入和删除效率很低,按索引访问元素很快
  • deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈
from collections import deque

# 创建一个空的 deque
dq = deque()

# 从右边添加元素
dq.append(1)
dq.append(2)

# 从左边添加元素
dq.appendleft(0)

# 从右边删除元素
dq.pop()

# 从左边删除元素
dq.popleft()

print(dq)  # 输出:deque([1])

defaultdict

defaultdict 是一个字典子类,它提供一个默认值,当键不存在时,返回指定的默认值。这对于避免 KeyError 非常有用。

defaultdict的其他行为跟dict是完全一样的。

from collections import defaultdict

# 创建一个 defaultdict,指定默认值为 int(默认值是 0)
dict0 = defaultdict(int)

# 向字典中添加元素
dict0['a'] += 1
dict0['b'] += 2

print(dict0)  # 输出:defaultdict(<class 'int'>, {'a': 1, 'b': 2})

# 如果访问一个不存在的键,返回默认值
print(dict0['c'])  # 输出:0

OrderedDict

OrderedDict 是一个有序字典,保持字典元素的插入顺序。通常标准字典是无序的,但 OrderedDict 会记住元素的插入顺序 而非 key 的顺序。

from collections import OrderedDict

# 创建一个有序字典
od = OrderedDict()

# 向字典中添加元素
od['apple'] = 3
od['banana'] = 2
od['orange'] = 1

# 打印字典
print(od)  # 输出:OrderedDict([('apple', 3), ('banana', 2), ('orange', 1)])

# 迭代时保持插入顺序
for key, value in od.items():
    print(key, value)
       
    
# 创建一个有序字典
od = OrderedDict()

# 向字典中添加元素
od['apple'] = 3
od['banana'] = 2
od['orange'] = 1

# 打印字典
print(od)  # 输出:OrderedDict([('apple', 3), ('banana', 2), ('orange', 1)])

# 迭代时保持插入顺序
for key, value in od.items():
    print(key, value)

ChainMap

ChainMap 用于将多个字典合并为一个视图,可以像访问普通字典一样访问多个字典的数据。

from collections import ChainMap

# 创建两个字典
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}

# 将两个字典合并
cm = ChainMap(dict1, dict2)

print(cm)  # ChainMap({'a': 1, 'b': 2}, {'b': 3, 'c': 4})
print(cm['a'])  # 1
print(cm['b'])  # 2(来自 dict1,因为 ChainMap 是从左到右查找)
print(cm['c'])  # 4(来自 dict2)

解析并获取命令行参数,如果没有获取到,使用默认参数

from collections import ChainMap
import os, argparse

"""
argparse 库用于处理命令行参数
argparse.ArgumentParser:创建一个 ArgumentParser 对象,用于处理命令行参数的解析
"""
parser = argparse.ArgumentParser()

"""
-s 或 --server: 接受一个值,指定服务。
-v 或 --version: 接受一个值,指定版本。
"""
parser.add_argument('-s', '--server')
parser.add_argument('-v', '--version')

"""
parse_args():解析命令行参数,返回一个命名空间对象,
其包含了命令行传入的参数的值。如果用户没有提供某个参数,则对应的值为 None。
"""
namespace = parser.parse_args()
command_line_args = { k: v for k, v in vars(namespace).items() if v }

# 构造缺省参数:
defaults = {
    'server': 'user-api',
    'version': '1.0.0'
}
# 组合成ChainMap:
combined = ChainMap(command_line_args, os.environ, defaults)
# 打印参数:
print('server=%s' % combined['server'])
print('version=%s' % combined['version'])

image.png

Counter

Counter 是一个字典的子类,用于统计可哈希对象的频率。它非常适合用来计数和统计元素的出现次数。

from collections import Counter

# 创建一个 Counter
c0 = Counter(['a', 'b', 'c', 'a', 'b', 'a'])

print(c0) # Counter({'a': 3, 'b': 2, 'c': 1})

# 获取某个元素的数量
print(c0['a']) # 3

# 获取出现最常见的元素
print(c0.most_common(1)) # [('a', 3)]

c1 = Counter('hello')
print(c1) # Counter({'l': 2, 'h': 1, 'e': 1, 'o': 1})
# 更新一次计数
c1.update('haha')
print(c1) # Counter({'h': 3, 'l': 2, 'a': 2, 'e': 1, 'o': 1})

argparse

sys.argv

sys.argv 是获取命令行参数的基础方法,适合用于简单的命令行参数传递和处理,但如果参数较复杂(比如需要处理可选参数、类型转换、帮助信息等),argparse 会更加方便和强大。

  • sys.argv 是一个列表,包含了从命令行传递给 Python 脚本的所有参数。
  • sys.argv[0] 是脚本本身的文件名(即执行的 Python 文件)。
  • 后续的元素(从 sys.argv[1] 开始)是传递给脚本的命令行参数。
bash
python test.py arg1 arg2 arg3

在脚本中,sys.argv 的内容会是:

python
import sys
print(sys.argv)

输出将会是:

['test.py', 'arg1', 'arg2', 'arg3']
  • sys.argv[0] = 'example.py'(脚本文件名)
  • sys.argv[1] = 'arg1'(第一个传递的参数)
  • sys.argv[2] = 'arg2'(第二个传递的参数)
  • sys.argv[3] = 'arg3'(第三个传递的参数)

argparse

argparse 是 Python 标准库中的一个模块,用于解析命令行参数。它提供了一个灵活且易用的方式来处理命令行输入,允许你定义期望的参数、自动生成帮助信息,并执行类型转换、验证等操作。

  • 创建解析器:使用 ArgumentParser() 创建一个解析器对象。
  • 添加参数:通过 add_argument() 方法来定义你期望的命令行参数。
  • 解析参数:使用 parse_args() 方法解析传递给脚本的命令行参数。
import argparse

def main():
    # 定义一个ArgumentParser实例:
    parser = argparse.ArgumentParser(
        prog='backup', # 程序名
        description='Backup MySQL database.', # 描述
        epilog='Copyright(r), 2025' # 说明信息
    )
    # 定义位置参数:
    parser.add_argument('outfile')
    # 定义关键字参数:
    parser.add_argument('--host', default='localhost')
    # 此参数必须为int类型:
    parser.add_argument('--port', default='3306', type=int)
    # 允许用户输入简写的-u:
    parser.add_argument('-u', '--user', required=True)
    parser.add_argument('-p', '--password', required=True)
    parser.add_argument('--database', required=True)
    # gz参数不跟参数值,因此指定action='store_true',意思是出现-gz表示True:
    parser.add_argument('-gz', '--gzcompress', action='store_true', required=False, help='Compress backup files by gz.')


    # 解析参数:
    args = parser.parse_args()

    # 打印参数:
    print('parsed args:')
    print(f'outfile = {args.outfile}')
    print(f'host = {args.host}')
    print(f'port = {args.port}')
    print(f'user = {args.user}')
    print(f'password = {args.password}')
    print(f'database = {args.database}')
    print(f'gzcompress = {args.gzcompress}')

if __name__ == '__main__':
    main()

image.png

base64

Base64 实际上就是一种查表的编码方法,使用 64 个字符的字符集进行编码,该字符集如下:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

每个字符使用六位(2^6 = 64)表示,因此它能够表示 64 个不同的值。

Base64编码会把3字节的二进制数据编码为4字节的文本数据,长度增加33%,好处是编码后的文本数据可以在邮件正文、网页等直接显示。

编码过程

  1. 原始字符串:Cat

  2. 转换为字节:每个字符转为 ASCII 码:

    • 'C' → 67 (二进制 01000011)
    • 'a' → 97 (二进制 01100001)
    • 't' → 116 (二进制 01110100) 所以原始数据是:01000011 01100001 01110100
  3. 将原始的二进制分成 4 组: 010000 110110 000101 110100

  4. 查表映射:

    • 010000 → 16(对应字符 Q)
    • 110110 → 54(对应字符 2)
    • 000101 → 5(对应字符 F)
    • 110100 → 52(对应字符 0) 所以编码后的字符串为:Q2F0。
  5. 没有填充

    • 这里没有出现不足三字节的情况,所以没有填充字符 =
  6. 填充字符

    • 如果原始数据不是 3 的倍数字节,编码后会填充 = 字符:
    • 如果有 1 个字节剩余,则添加两个 =
    • 如果有 2 个字节剩余,则添加一个 =

解码过程

解码是编码的逆过程。解码时会将 Base64 字符转换回原来的 6 位二进制数据,然后将这些数据重新合并成 8 位字节流,最终恢复出原始数据。

示例

import base64

def print_b64decode(s):
    s1 = base64.b64decode(s)
    print(s1.decode("utf-8"))

s0 = base64.b64encode("从今日起,王某便是元婴之下第一人.".encode("utf-8"))
print(s0)
print_b64decode(s0)

"""执行结果
b'5LuO5LuK5pel6LW377yM546L5p+Q5L6/5piv5YWD5am05LmL5LiL56ys5LiA5Lq6Lg=='
从今日起,王某便是元婴之下第一人.
"""

struct

用于打包和解包二进制数据,常用函数:

  • struct.pack(fmt, v1, v2, ...) : 将 Python 的值打包为二进制数据。
  • struct.unpack(fmt, buffer) : 从二进制数据中解包,返回一个元组。
  • struct.calcsize(fmt) : 返回打包格式字符串所需的字节数。

将浮点数转换为字节

import struct

s0 = b'123456'
print(s0)

int0 = 123
print(int0.to_bytes(2, byteorder='little'))
print(int0.to_bytes(2, byteorder='big'))

# 将浮点数转换为字节
f0 = 12.34
fb0 = struct.pack('f', f0)  # 'f' 表示 32 位浮点数

打包数据:将整数、浮点数和字符打包成二进制格式

"""
'if3s' 表示:
i:一个整数(4 字节)
f:一个浮点数(4 字节)
3s:一个长度为 3 的字节字符串
"""
data = struct.pack('if3s', 42, 3.14, b'abc')
print(data)  # 输出的是二进制数据

解包数据:按照相同的格式字符串解码

binary_data = struct.pack('if3s', 42, 3.14, b'abc')
unpacked_data = struct.unpack('if3s', binary_data)
print(unpacked_data)  # 输出: (42, 3.140000104904175, b'abc')

计算格式字符串所占的字节数

size = struct.calcsize('if3s')
print(size)  # 输出: 11 (4 字节整数 + 4 字节浮点数 + 3 字节字符串)

hashlib

hashlib 模块提供了许多常见的加密哈希算法,以使用如 MD5、SHA-1、SHA-256 等算法来计算字符串或二进制数据的哈希值

import hashlib

md5 = hashlib.md5()
md5.update("天让你死,我也要把你抢回来!".encode("utf-8"))
print(md5.hexdigest())  # daabec1fa6fdd9f2557de17317cb5931

md5 = hashlib.md5()
md5.update("天让你死,".encode("utf-8"))
md5.update("我也要把你抢回来!".encode("utf-8"))
print(md5.hexdigest())  # daabec1fa6fdd9f2557de17317cb5931

sha1 = hashlib.sha1()
sha1.update("顺为凡,逆则仙,只在心中一念间".encode("utf-8"))
print(sha1.hexdigest()) # acadf87861e261fc5107774c9642b6fcdfeea52b
sha1 = hashlib.sha1()
sha1.update("顺为凡,逆则仙,".encode("utf-8"))
sha1.update("只在心中一念间".encode("utf-8"))
print(sha1.hexdigest()) # acadf87861e261fc5107774c9642b6fcdfeea52b

hmac

为了防止黑客通过彩虹表反推原始口令,我们需要在计算哈希时加入盐值(salt),使得相同的输入产生不同的哈希。通常做法是计算 md5(message + salt)。然而,HMAC(Keyed-Hashing for Message Authentication)算法将盐值视为“口令”,并在计算过程中将其与消息一起哈希。HMAC算法通用,适用于任何哈希算法(如MD5、SHA-1),并提供更标准和安全的方式。Python的 hmac 模块实现了这一标准算法。

import hashlib
import hmac

key = b'secret000'
message = '我一生逆天,不甘心命运摆弄,欲成为天地之主,欲踏碎这天,轰灭这地!'.encode('utf-8')
h = hmac.new(key, message, digestmod=hashlib.sha256)
print(h.hexdigest())

itertools

itertools 模块为处理迭代器提供了丰富的工具,尤其适用于需要高效地生成组合、排列、笛卡尔积等场景。它的懒加载特性使得处理大型数据集时特别高效,避免了不必要的内存消耗。

import itertools

# count(start=0, step=1)
# 返回一个无限迭代器,从 start 开始,每次递增 step,默认是从 0 开始,步长为 1。
counter = itertools.count(1)
print(next(counter))
print(next(counter))

counter = itertools.count(10, 20)
print(next(counter))
print(next(counter))

# 创建一个无限的迭代器,重复遍历输入的可迭代对象。
cs = itertools.cycle([1, 2])
print(next(cs))  # 1
print(next(cs))  # 2
print(next(cs))  # 1

cs = itertools.cycle("这雨,出生于天,死于大地。中间的过程,便是人生。")
print(next(cs))
print(next(cs))

# repeat(object, times=None)
# 返回一个迭代器,重复返回同一个对象 times 次。如果 times 不指定,则默认无限次重复。
r0 = itertools.repeat("王林", 2)
print(next(r0))
print(next(r0))

items = ['王林', '李慕婉', '红蝶']

# permutations(iterable, r=None)
# 返回输入元素的所有排列(按顺序的排列)。如果给定参数 r,则返回所有 r 长度的排列。
perms = itertools.permutations(items, 2)
for perm in perms:
    print(perm)

# combinations(iterable, r)
# 返回输入元素的所有 r 长度的组合,组合中的元素是无序的。
import itertools
combs = itertools.combinations(items, 2)
for c in combs:
    print(c)

# combinations_with_replacement(iterable, r)
# 返回所有 r 长度的组合,并允许元素重复。
combs = itertools.combinations_with_replacement(items, 2)
for c in combs:
    print(c)

# product(*iterables, repeat=1)
# 返回多个可迭代对象的笛卡尔积。可以通过 repeat 参数指定重复次数。
items = ['王林', '李慕婉']
prod = itertools.product(items, repeat=2)
for p in prod:
    print(p)

# chain(*iterables)
# 将多个可迭代对象连接成一个大的迭代器,按顺序返回每个元素。
chain_obj = itertools.chain('ABC', '123')
print(list(chain_obj))

# zip_longest(*iterables, fillvalue=None)
# 类似 zip(),但会填充较短的可迭代对象,使它们的长度一致,直到最长的迭代器耗尽。
a = [1, 2, 3]
b = ['a', 'b']
zipped = itertools.zip_longest(a, b, fillvalue='x')
print(list(zipped))


# islice(iterable, start, stop, step)
# 返回一个迭代器,表示输入可迭代对象的一个切片,类似于切片操作,但是它是懒加载的(按需计算)。
items = range(10)
sliced = itertools.islice(items, 2, 8, 2)
print(list(sliced))

# groupby()把迭代器中相邻的重复元素挑出来放在一起:
for key,group in itertools.groupby("王王麻麻子小林子"):
    print(key, list(group))
"""执行结果
王 ['王', '王']
麻 ['麻', '麻']
子 ['子']
小 ['小']
林 ['林']
子 ['子']
""

contextlib

with 语句

在读写文件完毕后可以使用try...finally需要正确关闭,但是写try...finally的过程比较繁琐,而Python提供的 with 语句可以更方便地使用资源,而不必担心资源没有关闭。

with open('/path/to/file', 'r') as f:
    f.read()

但并不是只有open()函数返回的fp对象才能使用with语句。实际上,任何对象,只要正确实现了上下文管理,就可以用于with语句。

而实现上下文管理是通过__enter____exit__这两个方法实现的

class Work(object):
    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print('Begin do work')
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type:
            print('Error do work')
        else:
            print('End do work')

    def do_work(self):
        print('%s do work ...' % self.name)

with Work('Bob') as q:
    q.do_work()
    
"""执行结果
Begin do work
Bob do work ...
End do work
"""

@contextmanager

编写__enter____exit__仍然很繁琐,而contextlib提供了更简单的写法

class Work(object):
    def __init__(self, name):
        self.name = name
    def do_work(self):
        print('%s do work ...' % self.name)
"""
@contextmanager这个decorator接受一个generator,
用yield语句把with ... as var把变量输出出去,
然后,with语句就可以正常地工作了
"""
@contextmanager
def cwork(name):
    print('Begin do work')
    work = Work(name)
    yield work
    print('End do work')

with cwork('Bob') as worker:
    worker.do_work()
    

如果希望在某段代码执行前后自动执行特定代码,也可以用@contextmanager实现。

@contextmanager
def tag(name):
    print("<%s>" % name)
    yield
    print("</%s>" % name)

with tag("h1"):
    print("hello")
    print("world")

"""执行结果:
<h1>
hello
world
</h1>
"""

"""执行顺序:
with语句首先执行yield之前的语句,因此打印出<h1>;
yield调用会执行with语句内部的所有语句,因此打印出hello和world;
最后执行yield之后的语句,打印出</h1>。
"""

@closing

如果对象没有实现上下文,就不能用with语句。此时可以用closing()来把该对象变为上下文对象。

from contextlib import closing, contextmanager
from urllib.request import urlopen

with closing(urlopen('https://www.python.org')) as page:
    for line in page:
        print(line)     

closing也是一个经过@contextmanager装饰的generator,这个generator的实现其实非常简单;可将任意对象变为上下文对象,并支持with语句

@contextmanager
def closing(thing):
    try:
        yield thing
    finally:
        thing.close()

urllib

urllib 提供了用于操作 URL(Uniform Resource Locator)以及网络请求的功能。urllib 模块的主要作用是处理 HTTP 请求、解析和构建 URL,以及进行编码和解码操作。

urllib 模块的子模块:

  • urllib.request: 用于打开和读取 URLs(HTTP、FTP 等)。
  • urllib.parse: 用于解析 URL,并进行 URL 编码和解码。
  • urllib.error: 包含处理 HTTP 错误的异常类。
  • urllib.robotparser: 用于解析 robots.txt 文件。
  • urllib.response: 用于处理响应(通常不需要直接使用,已集成在 urllib.request 中)。

get

req = request.urlopen('http://www.python.org')
req.add_header = ('User-agent', 'Mozilla/5.0')
with req as response:
    print(f"Status: {response.status} {response.reason}")
    for k, v in response.getheaders():
        print(f"{k}:{v}")
    # print(response.read().decode('utf-8'))

ProxyHandler

如果需要通过Proxy去访问网站,可使用ProxyHandler来处理

proxy = "127.0.0.1:7890"
# proxy = "http://username:password@your_proxy_address:port"
proxy_handler = request.ProxyHandler({
    'http': proxy,
    'https': proxy
})

# 创建 opener
opener = request.build_opener(proxy_handler)
# 发送请求
response = opener.open("http://www.python.org")
# 读取响应内容
print(response.read())

XML

操作XML有两种方法:DOM和SAX。DOM将整个XML加载到内存,解析为树,优点是可以任意遍历,但占用内存大且解析慢;SAX是流式解析,占用内存小且解析快,但需要手动处理事件。通常优先考虑SAX。

在Python中,使用SAX解析XML很简单,只需实现 start_elementend_elementchar_data 三个事件处理函数即可。

from xml.parsers.expat import ParserCreate

class DefaultSaxHandler(object):
    def start_element(self, name, attrs):
        print('start_element: %s, attrs: %s' % (name, str(attrs)))

    def end_element(self, name):
        print('end_element: %s' % name)

    def char_data(self, text):
        print('char_data: %s' % text)
            

xml = r'''<?xml version="1.0"?>
<ol>
    <li><a href="/python">Python</a></li>
    <li><a href="/ruby">Ruby</a></li>
</ol>
'''

handler = DefaultSaxHandler()
parser = ParserCreate()
parser.StartElementHandler = handler.start_element
parser.EndElementHandler = handler.end_element
parser.CharacterDataHandler = handler.char_data
parser.Parse(xml)

HTMLParser

HTML本质上是XML的子集,但是HTML的语法没有XML那么严格,所以不能用标准的DOM或SAX来解析HTML。而Python提供了HTMLParser可非常方便地解析HTML

from html.parser import HTMLParser

class MyHTMLParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print('<%s>' % tag)

    def handle_endtag(self, tag):
        print('</%s>' % tag)

    def handle_startendtag(self, tag, attrs):
        print('<%s/>' % tag)

    def handle_data(self, data):
        print(data)

    def handle_comment(self, data):
        print('<!--', data, '-->')

    def handle_entityref(self, name):
        print('&%s;' % name)

    def handle_charref(self, name):
        print('&#%s;' % name)



parser = MyHTMLParser()
parser.feed('''<html>
<head></head>
<body>
<!-- test html parser -->
    <p>Some <a href="#">html</a> HTML&nbsp;tutorial...<br>END</p>
</body></html>''')