一、pyparsing 库概述
pyparsing 是一款纯 Python 编写的递归下降解析库,无需编写复杂正则表达式,即可实现文本、自定义语法、配置文件、日志等内容的结构化解析。其核心原理是通过组合基础解析元素构建语法规则,自上而下完成文本解析。该库轻量无依赖、上手简单,适合非专业解析器开发者快速实现解析需求;缺点是解析大型复杂语法时性能弱于专用解析器。采用 MIT License,可自由商用与修改。

二、pyparsing 基础安装与快速入门
2.1 安装 pyparsing
pyparsing 不依赖第三方库,直接通过 pip 即可完成安装,命令如下:
pip install pyparsing
安装完成后,在 Python 脚本中导入即可使用:
import pyparsing as pp
2.2 基础解析元素介绍
pyparsing 提供了大量基础解析单元,通过组合这些单元就能实现复杂解析逻辑,常用基础元素:
pp.Word:匹配连续字符(字母、数字、自定义字符集)pp.Suppress:匹配内容但不加入解析结果,用于过滤符号pp.OneOrMore:匹配一次或多次指定规则pp.ZeroOrMore:匹配零次或多次指定规则pp.Optional:匹配可选内容pp.Group:将解析结果分组,提升结构可读性
2.3 最简解析示例
以最简单的「匹配数字」为例,快速体验 pyparsing 解析流程:
import pyparsing as pp
# 定义解析规则:匹配连续数字
number = pp.Word(pp.nums)
# 待解析文本
text = "20250630"
# 执行解析
result = number.parseString(text)
# 输出解析结果
print("解析结果:", result)
print("结果类型:", type(result))
print("结果取值:", result[0])
代码说明:
- 使用
pp.nums定义数字字符集,pp.Word匹配连续数字 parseString是核心解析方法,传入待解析文本即可执行- 解析结果为
ParseResults类型,可像列表一样取值
运行结果:
解析结果: ['20250630']
结果类型: <class 'pyparsing.ParseResults'>
结果取值: 20250630
三、pyparsing 核心语法与进阶用法
3.1 处理带符号的文本(过滤无用字符)
实际解析中常出现括号、逗号、空格等符号,使用 pp.Suppress 可自动过滤:
import pyparsing as pp
# 定义规则:解析 (123,456) 格式,过滤括号和逗号
left = pp.Suppress("(")
right = pp.Suppress(")")
comma = pp.Suppress(",")
number = pp.Word(pp.nums)
# 组合规则:左括号 + 数字 + 逗号 + 数字 + 右括号
coord = left + number + comma + number + right
# 解析坐标文本
text = "(100,200)"
result = coord.parseString(text)
print("解析后的坐标:", result)
print("X坐标:", result[0])
print("Y坐标:", result[1])
代码说明:Suppress 仅匹配不保留,解析结果自动剔除符号,直接获取有效数据。
运行结果:
解析后的坐标: ['100', '200']
X坐标: 100
Y坐标: 200
3.2 解析带名称的键值对
解析配置文件常用的「名称=值」格式,可通过命名结果直接按键取值:
import pyparsing as pp
# 定义规则:变量名 = 数值
var_name = pp.Word(pp.alphas) # 字母开头的变量名
equal = pp.Suppress("=")
value = pp.Word(pp.nums + pp.alphas + ".") # 支持字母、数字、小数点
# 组合规则并命名
expr = var_name("key") + equal + value("value")
# 解析配置行
text = "version=3.10.4"
result = expr.parseString(text)
print("完整结果:", result)
print("键名:", result.key)
print("值:", result.value)
代码说明:
在解析元素后加 ("名称") 可命名结果,直接通过属性名取值,比索引更直观。
运行结果:
完整结果: ['version', '3.10.4']
键名: version
值: 3.10.4
3.3 分组解析复杂结构
当解析多层结构时,使用 pp.Group 对结果分组,避免结果扁平化:
import pyparsing as pp
# 基础规则
number = pp.Word(pp.nums)
colon = pp.Suppress(":")
comma = pp.Suppress(",")
# 定义学生信息:姓名:成绩,成绩,成绩
score = number
name = pp.Word(pp.alphas)
student = pp.Group(name + colon + pp.OneOrMore(score + pp.Optional(comma)))
# 解析多个学生信息
text = "Tom:90,85,95 Jerry:88,92,90"
result = pp.OneOrMore(student).parseString(text)
print("完整解析结果:", result)
print("第一个学生:", result[0])
print("第一个学生姓名:", result[0][0])
print("第一个学生成绩:", result[0][1:])
代码说明:Group 会将内部解析结果打包为子列表,多层结构清晰可辨。
运行结果:
完整解析结果: [['Tom', '90', '85', '95'], ['Jerry', '88', '92', '90']]
第一个学生: ['Tom', '90', '85', '95']
第一个学生姓名: Tom
第一个学生成绩: ['90', '85', '95']
3.4 自定义解析动作(转换数据类型)
pyparsing 默认解析结果为字符串,可通过 setParseAction 自定义处理函数,自动转换类型:
import pyparsing as pp
# 定义转换函数:将字符串转为整数
def to_int(tokens):
return int(tokens[0])
# 定义数字规则,并绑定转换动作
number = pp.Word(pp.nums).setParseAction(to_int)
# 解析并查看类型
text = "12345"
result = number.parseString(text)
print("解析结果:", result)
print("结果类型:", type(result[0]))
代码说明:setParseAction 接收函数,解析完成后自动执行函数处理结果,实现类型转换、校验、格式化等功能。
运行结果:
解析结果: [12345]
结果类型: <class 'int'>
四、pyparsing 实战案例:简易日志解析器
4.1 案例需求
解析程序日志,提取时间、日志级别、模块、信息,输出结构化字典。
日志格式:
[2025-06-30 10:00:00] [INFO] [Database] 连接成功
[2025-06-30 10:05:00] [ERROR] [Network] 请求超时
4.2 解析代码实现
import pyparsing as pp
# 过滤括号
lbracket = pp.Suppress("[")
rbracket = pp.Suppress("]")
# 定义各部分规则
time_str = pp.Word(pp.nums + "-: ") # 时间:2025-06-30 10:00:00
level = pp.Word(pp.alphas) # 日志级别:INFO/ERROR
module = pp.Word(pp.alphas) # 模块名
message = pp.restOfLine() # 剩余所有内容作为日志信息
# 组合完整规则并命名
log_expr = (
lbracket + time_str("time") + rbracket
+ lbracket + level("level") + rbracket
+ lbracket + module("module") + rbracket
+ message("message")
)
# 测试日志
log1 = "[2025-06-30 10:00:00] [INFO] [Database] 连接成功"
log2 = "[2025-06-30 10:05:00] [ERROR] [Network] 请求超时"
# 解析并转为字典
def parse_log(log_text):
res = log_expr.parseString(log_text)
return {
"time": res.time,
"level": res.level,
"module": res.module,
"message": res.message.strip()
}
# 输出结果
print(parse_log(log1))
print(parse_log(log2))
代码说明:
restOfLine()匹配行剩余所有内容,适合解析末尾不定长信息- 封装解析函数,直接返回结构化字典,方便后续数据处理
- 命名规则让代码可读性极高,无需记忆索引位置
运行结果:
{'time': '2025-06-30 10:00:00', 'level': 'INFO', 'module': 'Database', 'message': '连接成功'}
{'time': '2025-06-30 10:05:00', 'level': 'ERROR', 'module': 'Network', 'message': '请求超时'}
五、pyparsing 实战案例:自定义数学表达式解析器
5.1 案例需求
实现支持加减乘除、括号、多位数的简易表达式解析,并计算结果。
支持格式:
1+2*3
(10+5)*2
8/4+6
5.2 解析与计算代码
import pyparsing as pp
# 数字转换为整数
number = pp.Word(pp.nums).setParseAction(lambda t: int(t[0]))
# 定义运算符
plus = pp.Literal("+")
minus = pp.Literal("-")
mult = pp.Literal("*")
div = pp.Literal("/")
# 定义括号
lpar = pp.Suppress("(")
rpar = pp.Suppress(")")
# 定义表达式优先级:括号 > 乘除 > 加减
expr = pp.infixNotation(
number,
[
(pp.oneOf("* /"), 2, pp.opAssoc.LEFT), # 乘除,左结合
(pp.oneOf("+ -"), 2, pp.opAssoc.LEFT), # 加减,左结合
]
)
# 计算函数
def calculate(expression):
return eval(str(expr.parseString(expression)[0]))
# 测试
expr1 = "1+2*3"
expr2 = "(10+5)*2"
expr3 = "8/4+6"
print(f"{expr1} = {calculate(expr1)}")
print(f"{expr2} = {calculate(expr2)}")
print(f"{expr3} = {calculate(expr3)}")
代码说明:
infixNotation专门处理中缀表达式,自动处理优先级与结合性- 无需手动编写复杂递归逻辑,pyparsing 自动完成语法树构建
- 结合 eval 实现计算,也可自定义函数实现安全计算
运行结果:
1+2*3 = 7
(10+5)*2 = 30
8/4+6 = 8.0
六、pyparsing 实战案例:配置文件解析器
6.1 案例需求
解析类 INI 简易配置文件,提取节、键值对,生成嵌套字典。
配置文件内容:
[User]
name=test
age=20
[Database]
host=127.0.0.1
port=3306
6.2 解析代码
import pyparsing as pp
# 规则定义
lbracket = pp.Suppress("[")
rbracket = pp.Suppress("]")
equal = pp.Suppress("=")
# 节名:[User]
section = pp.Group(lbracket + pp.Word(pp.alphas)("section") + rbracket)
# 键值对:key=value
key = pp.Word(pp.alphas)
value = pp.restOfLine()
kv = pp.Group(key + equal + value)
# 配置文件:节 + 多个键值对
config = pp.Dict(pp.OneOrMore(section | kv))
# 测试配置文本
conf_text = """
[User]
name=test
age=20
[Database]
host=127.0.0.1
port=3306
"""
# 解析
result = config.parseString(conf_text)
# 输出嵌套字典
print("User 配置:", result.User)
print("Database 配置:", result.Database)
print("用户名:", result.User.name)
print("数据库端口:", result.Database.port)
代码说明:
Dict可将解析结果自动转为字典结构,支持链式取值- 支持多节配置,结构清晰,适合解析自定义配置文件
运行结果:
User 配置: {'name': 'test', 'age': '20'}
Database 配置: {'host': '127.0.0.1', 'port': '3306'}
用户名: test
数据库端口: 3306
七、相关资源
- Pypi地址:https://pypi.org/project/pyparsing/
- Github地址:https://github.com/pyparsing/pyparsing
- 官方文档地址:https://pyparsing-docs.readthedocs.io/
关注我,每天分享一个实用的Python自动化工具。
