Python 正则表达式实战指南:从入门到精通(12 个高频案例)(三)

正则表达式是 Python 文本处理的 "瑞士军刀",无论是数据校验、信息提取还是文本清洗,都能大幅提升效率。本文整理了 12 个日常开发中高频使用的实战案例,从基础验证到复杂解析,全程带代码、带思路,小白也能跟着练!

一、前言:为什么要学正则表达式?

在 Python 开发中,我们经常遇到这些场景:

  • 验证用户输入的手机号、邮箱是否合法
  • 从杂乱文本中提取身份证号、金额、链接
  • 批量替换文本中的敏感信息(如手机号打码)
  • 解析日志文件、HTML 页面中的关键数据

这些需求用普通字符串方法(find、split)实现起来繁琐且易出错,而正则表达式能通过简洁的语法实现复杂匹配逻辑,一行代码搞定重复工作!

本文基于 Python 内置 re 模块,聚焦实战场景,每个案例都包含「需求分析→正则思路→完整代码→结果解析」

二、基础验证类(表单 / 数据校验必备)

案例 1:验证中国大陆手机号

需求 :判断输入是否为 11 位有效手机号(支持 13/14/15/17/18/19 号段)正则思路

  • ^1:强制开头为数字 1(手机号专属前缀)
  • 3-9\]:第二位为 3-9(排除 12/11 等无效号段)

  • $:结尾符(避免字符串后接多余字符)

python

复制代码
import re

def is_valid_phone(phone):
    # 预编译正则,多次调用更高效
    pattern = re.compile(r"^1[3-9]\d{9}$")
    return pattern.match(phone) is not None

# 测试用例
test_phones = [
    "13812345678",  # 有效
    "19987654321",  # 有效
    "1234567890",   # 无效(不足11位)
    "138abc12345",  # 无效(含非数字)
    "138123456789"  # 无效(超过11位)
]

for phone in test_phones:
    status = "✅ 有效" if is_valid_phone(phone) else "❌ 无效"
    print(f"{phone} → {status}")

输出结果:13812345678 → ✅ 有效19987654321 → ✅ 有效1234567890 → ❌ 无效138abc12345 → ❌ 无效138123456789 → ❌ 无效

案例 2:验证邮箱格式(支持多级域名)

需求 :兼容常见邮箱格式(如 abc@qq.com、user.name_123@gmail.com、test@163.com.cn)正则思路

  • 用户名:[a-zA-Z0-9_.+-]+(支持字母、数字、下划线、点、加减号)
  • @:固定分隔符
  • 域名:[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+(支持多级域名,如 .com.cn

python

复制代码
def is_valid_email(email):
    pattern = re.compile(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$")
    return pattern.match(email) is not None

# 测试用例
test_emails = [
    "abc@qq.com",
    "user.name_123@gmail.com",
    "test@163.com.cn",
    "abc@.com",  # 域名开头不能是.
    "a@b.c"      # 极简合法格式
]

for email in test_emails:
    status = "✅ 有效" if is_valid_email(email) else "❌ 无效"
    print(f"{email} → {status}")

案例 3:验证 18 位身份证号

需求 :支持最后一位为数字或大写 X(身份证校验码规则)正则思路

  • ^\d {17}:前 17 位必须是数字
  • 0-9X\]:第 18 位为数字或大写 X

python

复制代码
def is_valid_id_card(id_card):
    pattern = re.compile(r"^\d{17}[0-9X]$")
    return pattern.match(id_card) is not None

# 测试用例
test_ids = [
    "110101199001011234",
    "11010119900101123X",
    "11010119900101123",  # 不足18位
    "11010119900101123x"  # 小写x无效
]

for id in test_ids:
    status = "✅ 有效" if is_valid_id_card(id) else "❌ 无效"
    print(f"{id} → {status}")

三、数据提取类(文本解析核心技能)

案例 4:从文本中提取所有有效手机号

需求 :忽略无关字符,批量抓取文本中的 11 位手机号核心技巧:用 re.findall () 匹配所有符合规则的子串(去掉 ^ 和 $ 不限制位置)

python

复制代码
text = """
联系我:13812345678(工作),私人电话19987654321,
备用号1234567890(无效),还有13579246801,紧急联系人18800001111
"""

# 提取所有有效手机号
phones = re.findall(r"1[3-9]\d{9}", text)
print("提取到的手机号:", phones)
# 输出:['13812345678', '19987654321', '13579246801', '18800001111']

案例 5:从身份证号提取出生日期

需求 :从 18 位身份证中提取「年 - 月 - 日」格式日期(身份证第 7-14 位为出生日期)核心技巧:用分组捕获年、月、日,再拼接格式

python

复制代码
def get_birthday_from_id(id_card):
    # 分组捕获年、月、日
    pattern = re.compile(r"^\d{6}(\d{4})(\d{2})(\d{2})\d{4}[0-9X]$")
    result = pattern.match(id_card)
    if result:
        return f"{result.group(1)}-{result.group(2)}-{result.group(3)}"
    return "❌ 无效身份证号"

# 测试
id_card = "110101199001011234"
print("出生日期:", get_birthday_from_id(id_card))
# 输出:出生日期:1990-01-01

案例 6:提取 HTML 中的所有链接

需求 :从 HTML 文本中抓取 <a href="..."> 标签中的 URL核心技巧:非贪婪匹配 .*? 避免匹配多个链接时 "贪多"

python

复制代码
html_text = '''
<div class="nav">
    <a href="https://www.baidu.com">百度</a>
    <a href="https://www.python.org">Python官网</a>
    <a href="https://github.com" target="_blank">GitHub</a>
</div>
'''

# 提取所有href属性值
urls = re.findall(r'<a href="(.*?)">', html_text)
print("提取到的链接:")
for url in urls:
    print(f"🔗 {url}")

案例 7:提取文本中的所有金额

需求 :支持整数(如 99)、小数(如 123.45)、带人民币符号(如 ¥66.8)的金额提取核心技巧:用 ¥? 匹配可选的人民币符号,\d+.?\d* 匹配整数 / 小数

python

复制代码
text = "价格:99元,优惠后123.45元,限时¥66.8,最高补贴1000元,无门槛券0.5元"

# 提取金额数字(忽略单位)
amounts = re.findall(r"¥?(\d+\.?\d*)", text)
print("提取到的金额:", amounts)  # ['99', '123.45', '66.8', '1000', '0.5']

# 转换为浮点数便于计算
amounts_float = [float(amt) for amt in amounts]
print("总金额:", sum(amounts_float))  # 输出:1299.75

四、文本替换类(批量清洗 / 修改文本)

案例 8:手机号中间 4 位打码(隐私保护)

需求 :将手机号 13812345678 转为 138****5678核心技巧:分组捕获前 3 位和后 4 位,替换时引用分组

python

复制代码
text = "手机号:13812345678,备用号19987654321,紧急联系人18800001111"

# 分组捕获前3位(\d{3})和后4位(\d{4}),中间4位替换为****
masked_text = re.sub(r"(\d{3})\d{4}(\d{4})", r"\1****\2", text)
print("打码后
print("打码后:", masked_text)
# 输出:手机号:138****5678,备用号199****4321,紧急联系人188****1111

案例 9:清除文本中的所有标点符号

需求 :保留汉字、字母、数字,去掉逗号、句号、感叹号等标点核心技巧:用 [^a-zA-Z0-9\u4e00-\u9fa5] 匹配 "非目标字符",替换为空

python

复制代码
text = "Hello!Python正则,真的很实用~ 你学会了吗?12345!"

# 清除所有标点(保留汉字、字母、数字)
clean_text = re.sub(r"[^a-zA-Z0-9\u4e00-\u9fa5]", "", text)
print("清洗后:", clean_text)
# 输出:HelloPython正则真的很实用你学会了吗12345

案例 10:批量替换日期格式(YYYY-MM-DD → MM/DD/YYYY)

需求 :将 2025-10-01 转为 10/01/2025核心技巧:分组捕获年、月、日,调整分组顺序替换

python

复制代码
text = "会议时间:2025-10-01,截止日期2025-12-31,发布于2024-09-15"

# 分组捕获(\d{4})-(\d{2})-(\d{2}),替换为\2/\3/\1
new_text = re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\2/\3/\1", text)
print("替换后:", new_text)
# 输出:会议时间:10/01/2025,截止日期12/31/2025,发布于09/15/2024

五、复杂场景实战(综合应用)

案例 11:解析日志文件中的 404 错误

需求 :从 Nginx 日志中提取 404 错误的 IP 和请求路径日志格式示例:192.168.1.1 - - [10/Oct/2025:14:30:00 +0800] "GET /index.html HTTP/1.1" 200 123410.0.0.1 - - [10/Oct/2025:14:31:00 +0800] "GET /nonexistent HTTP/1.1" 404 567192.168.1.2 - - [10/Oct/2025:14:32:00 +0800] "POST /api/data HTTP/1.1" 404 890

正则思路

  • 捕获 IP:(\d+.\d+.\d+.\d+)(匹配 IPv4 地址)
  • 捕获路径:"[A-Z]+ (.*?) HTTP/1.1"(匹配请求方法后的路径)
  • 筛选 404:"404"(只匹配状态码为 404 的记录)

python

复制代码
log_text = '''
192.168.1.1 - - [10/Oct/2025:14:30:00 +0800] "GET /index.html HTTP/1.1" 200 1234
10.0.0.1 - - [10/Oct/2025:14:31:00 +0800] "GET /nonexistent HTTP/1.1" 404 567
192.168.1.2 - - [10/Oct/2025:14:32:00 +0800] "POST /api/data HTTP/1.1" 404 890
'''

# 提取404错误的IP和路径
errors = re.findall(r"(\d+\.\d+\.\d+\.\d+).*?\"[A-Z]+ (.*?) HTTP/1.1\" 404", log_text)
print("404错误详情:")
for ip, path in errors:
    print(f"IP: {ip}, 请求路径: {path}")

输出结果:404 错误详情:IP: 10.0.0.1, 请求路径: /nonexistentIP: 192.168.1.2, 请求路径: /api/data

案例 12:验证并提取 URL 中的域名

需求 :从 URL 中提取主域名(如 https://www.baidu.com/s?wd=pythonbaidu.com正则思路

  • 匹配协议(可选):(?:https?://)?(非捕获分组,不提取协议)
  • 匹配域名:(www.)?([a-zA-Z0-9-]+.[a-zA-Z0-9-.]+)(捕获 www 前缀和主域名)
  • 忽略路径:/?.*(匹配域名后的路径和参数)

python

复制代码
def extract_domain(url):
    # 匹配URL并捕获域名
    pattern = re.compile(r"^(?:https?://)?(www\.)?([a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)/?.*$")
    result = pattern.match(url)
    if result:
        return result.group(2)  # 返回主域名(去掉www.)
    return "❌ 无效URL"

# 测试
urls = [
    "https://www.baidu.com/s?wd=python",
    "http://github.com",
    "www.python.org/doc",
    "baidu.com",
    "invalid-url"
]

for url in urls:
    print(f"{url} → 域名:{extract_domain(url)}")

输出结果https://www.baidu.com/s?wd=python → 域名:baidu.comhttp://github.com → 域名:github.comwww.python.org/doc → 域名:python.orgbaidu.com → 域名:baidu.cominvalid-url → 域名:❌ 无效 URL

六、正则实用技巧(避坑 + 效率提升)

  1. 预编译正则:频繁使用的正则用 re.compile () 预编译,减少重复编译开销
  2. 原始字符串:正则表达式用 r""包裹,避免反斜杠转义问题(如 r"\d"比"\d" 更安全)
  3. 非贪婪匹配 :不确定匹配长度时,用 .? 代替 .,避免 "匹配过度"
  4. 分组命名:复杂场景用 (?P<name>pattern) 命名分组,后续用 group ("name") 提取,更易维护
  5. 在线调试:遇到复杂正则,用 在线调试(选择 Python 语法)

七、总结

正则表达式的核心是 "用规则匹配文本",本文的 12 个案例覆盖了 80% 的日常开发场景。刚开始学习时可能觉得语法晦涩,但只要多练、多调试,熟悉常用符号(如 ^ $ \d [] () * + ?)的含义,就能快速上手。

如果遇到特定场景(如爬取数据、JSON 解析、复杂日志分析),可以基于本文的思路扩展正则规则,也可以留言交流你的需求,我会补充对应的实战案例!

最后,记住:正则不是越复杂越好,能简洁解决问题的就是好正则~

相关推荐
ZC跨境爬虫2 小时前
海南大学交友平台登录页开发实战day4(解决python传输并读取登录信息的问题)
开发语言·前端·python·flask·html
Wyawsl2 小时前
Python操作MySQL数据库
数据库·python·mysql
SuperEugene2 小时前
Python 异步 async/await:为什么 AI 框架大量使用?| 基础篇
开发语言·人工智能·python
SMF19192 小时前
【uv】Python包管理器uv安装和应用
开发语言·python·uv
gergul2 小时前
在llama-cpp-python中使用自己编译的llama.cpp,解决pip install llama-cpp-python报错
python·llama·llama.cpp·llamacpppython
深蓝轨迹2 小时前
#Python零基础机器学习入门教程
人工智能·python·机器学习
蓝色的杯子2 小时前
Python面试30分钟突击掌握-LeetCode1-Array
开发语言·python·面试
怪祝浙2 小时前
超简洁YOLO8n快速上手人员检测
python
财经资讯数据_灵砚智能2 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年4月10日
人工智能·python·信息可视化·自然语言处理·ai编程