正则表达式的优势
相比Beautiful Soup等解析工具的优势:
速度极快:直接模式匹配,不需要构建完整的DOM树
内存占用少:只提取需要的信息,不加载全部内容
适合大规模数据:处理百万级网页数据的首选方案
实际应用案例:
解析900万百度百科页面
解析1000万维基百科内容
处理速度比传统解析方式快数倍
正则表达式基础语法
核心组件
import re
# 1. 编译正则表达式模式
pattern = re.compile(r'正则表达式模式')
# compile()函数:将正则表达式编译成Pattern对象,提高匹配效率
# 一次编译,多次使用,避免重复解析模式串
# 2. 基本匹配方法
text = "待搜索的文本内容"
result = pattern.match(text) # 从字符串开头匹配
result = pattern.search(text) # 在整个字符串中搜索第一个匹配
results = pattern.findall(text) # 找到所有匹配项,返回列表
常用元字符表
# 字符类匹配
r'\d' # 匹配任何数字 (0-9)
r'\D' # 匹配任何非数字字符
r'\w' # 匹配任何字母、数字、下划线 (a-z, A-Z, 0-9, _)
r'\W' # 匹配任何非字母数字字符
r'\s' # 匹配任何空白字符 (空格、制表符、换行符等)
r'\S' # 匹配任何非空白字符
# 量词
r'+' # 匹配前面的字符1次或多次
r'*' # 匹配前面的字符0次或多次
r'?' # 匹配前面的字符0次或1次
r'{n}' # 精确匹配n次
r'{n,m}' # 匹配n到m次
# 特殊字符
r'.' # 匹配除换行符外的任何字符
r'^' # 匹配字符串开头
r'$' # 匹配字符串结尾
实战示例详解
示例1:提取数字
import re
# 原始文本
text = "价格是123.45元,数量有67个,总计890.12元"
# 1. 编译模式 - 匹配整数和小数
pattern = re.compile(r'\d+\.?\d*')
# \d+ : 匹配一个或多个数字
# \.? : 匹配0个或1个小数点(?表示可选)
# \d* : 匹配0个或多个数字(小数部分)
# 2. 查找所有数字
numbers = pattern.findall(text)
print("提取的数字:", numbers)
# 输出:['123.45', '67', '890.12']
# 3. 转换为浮点数进行计算
float_numbers = [float(num) for num in numbers]
print("数字总和:", sum(float_numbers))
示例2:文本分割
# 需要分割的文本
text = "apple1banana2cherry3date4elderberry"
# 编译分割模式 - 按数字分割
split_pattern = re.compile(r'\d+')
# \d+ : 匹配一个或多个连续数字作为分隔符
# 使用split方法分割
fruits = split_pattern.split(text)
print("分割结果:", fruits)
# 输出:['apple', 'banana', 'cherry', 'date', 'elderberry', '']
# 注意:最后有个空字符串,因为文本末尾没有内容
# 过滤空字符串
fruits = [fruit for fruit in fruits if fruit]
print("清理后:", fruits)
示例3:捕获分组
# HTML标签文本
html_text = '<h1>标题</h1><p>段落内容</p><div>区块内容</div>'
# 编译模式 - 捕获HTML标签和内容
tag_pattern = re.compile(r'<(\w+)>([^<]+)</\1>')
# <(\w+)> : 匹配开始标签,\w+捕获标签名
# ([^<]+) : 捕获标签内容,^<表示除了<之外的字符
# </\1> : 匹配结束标签,\1引用第一个捕获组
# 查找所有匹配项
matches = tag_pattern.findall(html_text)
print("标签和内容:")
for tag, content in matches:
print(f"标签: {tag}, 内容: {content}")
# 输出:
# 标签: h1, 内容: 标题
# 标签: p, 内容: 段落内容
# 标签: div, 内容: 区块内容
示例4:贪婪与非贪婪匹配
text = "数字123和456还有789"
# 贪婪匹配(默认)
greedy_pattern = re.compile(r'\d+')
greedy_result = greedy_pattern.findall(text)
print("贪婪匹配:", greedy_result)
# 输出:['123', '456', '789'] - 尽可能多地匹配数字
# 非贪婪匹配
non_greedy_pattern = re.compile(r'\d+?') # +? 表示非贪婪
non_greedy_result = non_greedy_pattern.findall(text)
print("非贪婪匹配:", non_greedy_result)
# 输出:['1', '2', '3', '4', '5', '6', '7', '8', '9'] - 尽可能少地匹配
# 实际应用:提取HTML中的内容
html = '<div>内容1</div><div>内容2</div>'
# 贪婪匹配问题
wrong_pattern = re.compile(r'<div>.*</div>')
print("错误的贪婪匹配:", wrong_pattern.findall(html))
# 输出:['<div>内容1</div><div>内容2</div>'] - 匹配了整个字符串
# 正确的非贪婪匹配
correct_pattern = re.compile(r'<div>(.*?)</div>')
print("正确的非贪婪匹配:", correct_pattern.findall(html))
# 输出:['内容1', '内容2'] - 分别匹配每个div的内容
实际应用场景
场景1:网页数据清洗
# 从网页抓取的原始文本
raw_html = """
<div class="price">¥123.45</div>
<span class="discount">8.5折</span>
<p class="description">商品描述:高质量产品</p>
"""
# 提取价格
price_pattern = re.compile(r'¥(\d+\.?\d*)')
prices = price_pattern.findall(raw_html)
print("提取的价格:", prices)
# 提取折扣
discount_pattern = re.compile(r'(\d+\.?\d*)折')
discounts = discount_pattern.findall(raw_html)
print("提取的折扣:", discounts)
# 清理HTML标签
clean_pattern = re.compile(r'<[^>]+>')
clean_text = clean_pattern.sub('', raw_html) # sub方法用于替换
print("清理后的文本:", clean_text.strip())
场景2:日志文件分析
# 服务器日志示例
log_lines = [
"2023-10-15 14:30:25 [INFO] 用户登录成功 user_id=12345",
"2023-10-15 14:31:02 [ERROR] 数据库连接失败 error_code=500",
"2023-10-15 14:31:15 [WARNING] 内存使用率达到85%"
]
# 编译日志解析模式
log_pattern = re.compile(r'(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) \[(\w+)\] (.+)')
# (\d{4}-\d{2}-\d{2}) : 捕获日期
# (\d{2}:\d{2}:\d{2}) : 捕获时间
# \[(\w+)\] : 捕获日志级别
# (.+) : 捕获日志消息
# 解析每条日志
for line in log_lines:
match = log_pattern.match(line)
if match:
date, time, level, message = match.groups()
print(f"日期: {date}, 时间: {time}, 级别: {level}, 消息: {message}")
性能优化技巧
import re
import time
# 大量文本数据
large_text = "测试文本123" * 100000
# 方法1:每次都编译(效率低)
def slow_method():
start_time = time.time()
for _ in range(1000):
matches = re.findall(r'\d+', large_text) # 每次都编译
end_time = time.time()
return end_time - start_time
# 方法2:预编译模式(效率高)
def fast_method():
pattern = re.compile(r'\d+') # 只编译一次
start_time = time.time()
for _ in range(1000):
matches = pattern.findall(large_text) # 重复使用编译好的模式
end_time = time.time()
return end_time - start_time
print(f"慢方法耗时: {slow_method():.4f}秒")
print(f"快方法耗时: {fast_method():.4f}秒")
学习建议
练习步骤
熟记基础语法:掌握常用元字符和量词的含义
在线练习:使用在线正则表达式测试工具
实际应用:从简单的文本提取开始,逐步处理复杂数据
性能意识:大数据量处理时注意预编译和内存管理
最佳实践
预编译模式:对于重复使用的正则表达式,使用
re.compile()
非贪婪匹配:需要精确匹配时使用
?
避免过度匹配分组捕获:使用括号
()
捕获需要的部分测试验证:复杂的正则表达式要充分测试边界情况
常见应用领域
数据清洗:清理网页抓取的脏数据
日志分析:解析服务器日志文件
文本处理:提取特定格式的信息
验证输入:验证邮箱、电话号码等格式
正则表达式是数据处理中的利器,虽然学习曲线较陡峭,但掌握后能显著提高数据处理效率,特别是在处理大规模文本数据时优势明显。