正则表达式详解

发布于:2025-05-31 ⋅ 阅读:(23) ⋅ 点赞:(0)

正则表达式的优势

相比Beautiful Soup等解析工具的优势

  1. 速度极快:直接模式匹配,不需要构建完整的DOM树

  2. 内存占用少:只提取需要的信息,不加载全部内容

  3. 适合大规模数据:处理百万级网页数据的首选方案

实际应用案例

  • 解析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}秒")

学习建议

练习步骤

  1. 熟记基础语法:掌握常用元字符和量词的含义

  2. 在线练习:使用在线正则表达式测试工具

  3. 实际应用:从简单的文本提取开始,逐步处理复杂数据

  4. 性能意识:大数据量处理时注意预编译和内存管理

最佳实践

  1. 预编译模式:对于重复使用的正则表达式,使用re.compile()

  2. 非贪婪匹配:需要精确匹配时使用?避免过度匹配

  3. 分组捕获:使用括号()捕获需要的部分

  4. 测试验证:复杂的正则表达式要充分测试边界情况

常见应用领域

  • 数据清洗:清理网页抓取的脏数据

  • 日志分析:解析服务器日志文件

  • 文本处理:提取特定格式的信息

  • 验证输入:验证邮箱、电话号码等格式

正则表达式是数据处理中的利器,虽然学习曲线较陡峭,但掌握后能显著提高数据处理效率,特别是在处理大规模文本数据时优势明显。


网站公告

今日签到

点亮在社区的每一天
去签到