99天精通Python正则表达式进阶之分组与断言

 更新时间:2026年09月11日 09:15:57   作者:Robot侠  
正则表达式Regex是一种文本处理工具,通过特殊字符组合定义匹配模式,用于字符串的查找、校验、提取和替换,这篇文章主要介绍了99天精通Python正则表达式进阶之分组与断言的相关资料,需要的朋友可以参考下

前言

在上一节课中,我们学会了用正则表达式匹配"一类"字符串(比如所有手机号、所有邮箱)。但有时候,我们的需求会更精细:

  • 我想匹配邮箱,但我只想要 @ 前面的用户名,不要后面的域名。
  • 我想匹配 HTML 标签 <h1>标题</h1>,但我必须确保前后的标签名是一致的(不能是 <h1>标题</h2>)。
  • 我想匹配 “Windows 10” 中的 “Windows”,但前提是它后面必须跟着 “10”(也就是不匹配 “Windows XP”)。

这就需要用到正则的进阶大招:分组 (Groups) 和 断言 (Assertions)。掌握了它们,你就能像外科手术刀一样精准地切割和处理文本。

本节内容:

  • 分组 () 与 group() 提取
  • 后向引用 \1:匹配重复出现的文本
  • re.sub 的高级替换技巧
  • 零宽断言:(?=...) 和 (?<=...)
  • 实战练习:HTML 解析与数据清洗

一、分组 (Grouping):精准提取

1.1 什么是分组?

在正则中,用圆括号 () 包起来的部分就是一个分组。
它的作用主要有两个:

  1. 整体操作:比如 (ab)+ 表示匹配 “ab” 重复多次(abab…),而不加括号 ab+ 表示 a 后面跟着多个 b(abbb…)。
  2. 独立提取:匹配成功后,可以单独获取括号内匹配到的内容。

1.2 使用 group() 获取内容

import re

text = "我的邮箱是 alice@google.com,请联系我。"
# 需求:提取用户名和域名

# 使用两个括号创建两个分组
pattern = r"(\w+)@(\w+\.\w+)"
match = re.search(pattern, text)

if match:
    print(match.group())   # alice@google.com (完整匹配)
    print(match.group(0))  # 同上
    
    print(match.group(1))  # alice (第一个括号的内容)
    print(match.group(2))  # google.com (第二个括号的内容)
    
    print(match.groups())  # ('alice', 'google.com') (所有分组组成的元组)

二、后向引用 (Backreference):前后一致

假设我们要匹配 HTML 标题标签,如 <h1>Hello</h1> 或 <h3>Hi</h3>。
如果我们写 <h\d>.*</h\d>,它可能会错误地匹配到 <h1>Hello</h2>(头尾不一致)。

为了确保结束标签和开始标签匹配,我们需要用到后向引用:\1 代表引用第一个分组匹配到的实际内容。

import re

htmls = ["<h1>Valid</h1>", "<h2>Wrong</h3>", "<h3>Also Valid</h3>"]

# <(h\d)> : 第一个分组,匹配 h1-h9
# .*?     : 中间内容(非贪婪)
# </\1>   : 引用第一个分组匹配到的内容(如果前面是h1,这里必须也是h1)
pattern = r"<(h\d)>.*?</\1>"

for html in htmls:
    if re.search(pattern, html):
        print(f"匹配成功: {html}")
    else:
        print(f"匹配失败: {html}")

# 输出:
# 匹配成功: <h1>Valid</h1>
# 匹配失败: <h2>Wrong</h3>
# 匹配成功: <h3>Also Valid</h3>

三、re.sub 的高级替换:调整顺序

re.sub(pattern, repl, string) 不仅可以把匹配到的内容替换成固定字符串,还可以调整内容顺序。

在 repl 参数中,使用 \1, \2 来引用分组。

# 需求:将日期格式从 "2026-01-20" 转换为 "01/20/2026" (月/日/年)

text = "Today is 2026-01-20."
# 分组1: 年, 分组2: 月, 分组3: 日
pattern = r"(\d{4})-(\d{2})-(\d{2})"

# 替换为:分组2/分组3/分组1
new_text = re.sub(pattern, r"\2/\3/\1", text)
print(new_text)
# Today is 01/20/2026.

四、零宽断言 (Lookaround):只看不吃

断言是一种特殊的正则,它只进行判断,不消耗字符(即匹配结果中不包含它)。

4.1 正向先行断言(?=...)

含义:匹配 xxx,但前提是 xxx 后面必须跟着 yyy。
语法:xxx(?=yyy)

场景:匹配 “Windows 10” 中的 “Windows”,但不匹配 “Windows XP”。

text = "Windows 10 is better than Windows XP."

# Windows(?= 10) -> 查找 Windows,要求后面紧跟 " 10"
# 注意:结果只返回 "Windows",不包含 " 10"
matches = re.findall(r"Windows(?= 10)", text)
print(matches) 
# ['Windows'] (只匹配了第一个)

4.2 正向后行断言(?<=...)

含义:匹配 yyy,但前提是 yyy 前面必须是 xxx。
语法:(?<=xxx)yyy

场景:匹配价格数字,但只匹配带 $ 符号的。如 $99 中的 99,不匹配 item 99。

text = "Price: $99, Quantity: 99"

# (?<=\$)\d+ -> 查找数字,要求前面紧跟 "$"
matches = re.findall(r"(?<=\$)\d+", text)
print(matches)
# ['99'] (只匹配了第一个99)

记忆口诀:

  • 先行 (=):往后看。
  • 后行 (<=):往前看(箭头向左)。

五、实战练习

练习1:提取超链接 URL

从 HTML 字符串中提取 href="..." 里面的链接地址。

html = '<a href="https://www.google.com" rel="external nofollow"  class="link">Google</a>'

# 方法1:使用分组提取
# 匹配 href="任意内容" rel="external nofollow" 
pattern1 = r'href="(.*?)" rel="external nofollow" '
print(re.findall(pattern1, html))
# ['https://www.google.com']

# 方法2:使用断言 (逼格更高,且不需要处理分组)
# 前面是 href=",后面是 " rel="external nofollow" 
pattern2 = r'(?<=href=").*?(?=" rel="external nofollow" )'
print(re.findall(pattern2, html))
# ['https://www.google.com']

练习2:密码强度验证

要求:

  1. 至少 6 位。
  2. 必须包含至少一个数字。
  3. 必须包含至少一个字母。

这是一个经典的断言应用场景(同时满足多个条件)。

def check_password(pwd):
    # ^             : 开头
    # (?=.*\d)      : 往后看,必须包含至少一个数字
    # (?=.*[a-zA-Z]): 往后看,必须包含至少一个字母
    # .{6,}         : 任意字符至少6位
    # $             : 结尾
    pattern = r"^(?=.*\d)(?=.*[a-zA-Z]).{6,}$"
    
    return re.match(pattern, pwd) is not None

print(check_password("123456"))   # False (没字母)
print(check_password("abcdef"))   # False (没数字)
print(check_password("abc12"))    # False (太短)
print(check_password("abc1234"))  # True

六、常见问题

Q1:非捕获分组(?:...)是什么?

有时候我们需要用括号来整体重复 (ab)+,但又不想让它占一个分组编号(不想被 group(1) 取到)。这时可以用 (?:ab)+。
它参与匹配,但不参与提取,效率稍微高一点点。

Q2:断言能匹配不定长的内容吗?

Python 的 re 模块中,后行断言 (?<=...) 里的内容必须是固定长度的。你不能写 (?<=a*)。但是第三方库 regex 支持不定长。

七、小结

关键要点:

  1. 想要提取某部分内容?用 ()。
  2. 想要确保前后内容一致?用 \1。
  3. 想要匹配"位置"而不是"字符"(比如只要数字不要单位)?用 断言。

八、课后作业

  1. Markdown 图片链接提取:Markdown 图片格式为 ![alt](url)。编写正则,提取出所有的 alt 文字和 url 链接。
  2. 数字千分位:将 “1234567890” 转换为 “1,234,567,890”。提示:使用 re.sub 和零宽断言(匹配"空位置",该位置后面紧跟的数字个数是3的倍数)。
  3. 日志提取:日志格式 [INFO] 2026-01-20 User login。请提取出:日志级别(INFO)、日期(2026-01-20) 和 消息(User login)。

总结

到此这篇关于Python正则表达式进阶之分组与断言的文章就介绍到这了,更多相关Python正则表达式分组与断言内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python之列表的插入&替换修改方法

    Python之列表的插入&替换修改方法

    今天小编就为大家分享一篇Python之列表的插入&替换修改方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-06-06
  • Pandas中DataFrame中.iloc 属性

    Pandas中DataFrame中.iloc 属性

    Pandas的iloc属性是基于整数位置的数据选择方法,它允许用户通过整数位置来选择DataFrame中的行和列,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-01-01
  • 20个超实用Python自动化脚本分享

    20个超实用Python自动化脚本分享

    在当今的快节奏工作环境中,自动化不再是一种奢侈,而是提高效率和精确性的必需手段,这篇文章为大家整理了20个超实用Python自动化脚本,希望对大家有所帮助
    2024-01-01
  • python流程图和思维导图实例代码

    python流程图和思维导图实例代码

    这篇文章主要给大家介绍了关于python流程图和思维导图的相关资料,学习python过程中,画流程图可以有效的帮助你梳理程序的逻辑,本文通过示例代码介绍的非常详细,需要的朋友可以参考下
    2021-08-08
  • 一篇文章带你了解python标准库--time模块

    一篇文章带你了解python标准库--time模块

    下面小编就为大家带来一篇python模块之time模块。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2021-08-08
  • Python处理中文标点符号大集合

    Python处理中文标点符号大集合

    中文文本中可能出现的标点符号来源比较复杂,通过匹配等手段对他们处理的时候需要格外小心,防止遗漏,下面小编给大家带来了Python处理中文标点符号大集合,感兴趣的朋友跟随脚本之家小编一起看看吧
    2018-05-05
  • Python中property函数用法实例分析

    Python中property函数用法实例分析

    这篇文章主要介绍了Python中property函数用法,结合实例形式分析了property函数的功能、参数、使用方法及相关操作注意事项,需要的朋友可以参考下
    2018-06-06
  • pyqt和pyside开发图形化界面

    pyqt和pyside开发图形化界面

    选择PyQt或PySide来开发图形界面是因为Python和Qt的跨平台特性,Qt5甚至支持iOS和Android,并且开发相同的软件,Python的效率是极高的,下面看使用示例
    2014-01-01
  • Python 中 sorted 如何自定义比较逻辑

    Python 中 sorted 如何自定义比较逻辑

    这篇文章主要介绍了Python中sorted如何自定义比较逻辑,帮助大家更好的理解和学习使用python,感兴趣的朋友可以了解下
    2021-02-02
  • python自动化测试selenium核心技术等待条件教程

    python自动化测试selenium核心技术等待条件教程

    这篇文章主要为大家介绍了python自动化测试selenium核心技术等待条件教程的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2021-11-11

最新评论