re 模块
Python 内置了 re 模块,它提供了对正则表达式的支持,所有操作都围绕这个模块展开。

re.match(): 从字符串的开头进行匹配,如果开头不匹配,则返回None。re.search(): 扫描整个字符串,返回第一个匹配到的结果。re.findall(): 扫描整个字符串,返回所有匹配到的结果,以列表形式。re.finditer(): 扫描整个字符串,返回一个迭代器,每次迭代返回一个匹配到的对象(更节省内存)。re.sub(): 替换字符串中匹配到的内容。
对于提取字符串,最常用的是 re.findall() 和 re.finditer()。
基础提取:re.findall()
这是最简单、最直接的方法,当你只需要获取匹配到的文本本身时,用它最方便。
语法
re.findall(pattern, string, flags=0)
pattern: 正则表达式模式。string: 要搜索的字符串。flags: 可选,用于控制正则表达式的匹配方式,如忽略大小写等。
示例 1:提取所有数字
import re text = "我的电话是13812345678,办公室电话是010-87654321,备用号码是9988。" # \d 匹配任意一个数字(0-9) pattern = r"\d+" # + 表示匹配前面的元素(这里是\d)一次或多次 matches = re.findall(pattern, text) print(matches) # 输出: ['13812345678', '010', '87654321', '9988']
示例 2:提取所有邮箱地址
import re text = "联系我们:邮箱1是zhangsan@example.com,邮箱2是li.si@work.org。" # \w 匹配单词字符(a-z, A-Z, 0-9, _) # . 在正则中是特殊字符,用\.来匹配字面上的点号 # + 表示匹配前面的元素一次或多次 # [a-z]+ 匹配一个或多个小写字母 pattern = r"[\w\.-]+@[\w\.-]+\.\w+" matches = re.findall(pattern, text) print(matches) # 输出: ['zhangsan@example.com', 'li.si@work.org']
进阶提取:使用捕获组
当你需要从匹配到的复杂模式中提取特定部分时,就需要用到捕获组,也就是括号 。
捕获组的工作原理是:正则表达式引擎会匹配整个模式,但同时会把括号内的内容单独保存起来。

示例 3:提取带区号的电话号码,并分离区号和号码
假设我们有这样的文本 "北京: 010-12345678",我们想分别得到区号 010 和号码 12345678。
import re
text = "北京: 010-12345678, 上海: 021-87654321"
# () 创建了两个捕获组
# 第一个捕获组 (\d+) 用于匹配区号
# 第二个捕获组 (\d+) 用于匹配电话号码
pattern = r"(\d+)-(\d+)"
matches = re.findall(pattern, text)
print(matches)
# 输出: [('010', '12345678'), ('021', '87654321')]
解释:
re.findall()在有多个捕获组时,会返回一个元组的列表,每个元组对应一次完整匹配,元组中的元素就是各个捕获组匹配到的内容。
最强大的提取:re.finditer() + Match 对象
re.finditer() 结合 Match 对象是功能最全、信息最丰富的提取方式,当你不仅需要提取文本,还需要知道文本在原字符串中的位置(索引)时,这个方法是无价的。
Match 对象的常用方法
.group(): 返回匹配到的字符串。.group(1),.group(2): 返回指定捕获组匹配到的字符串。.groups(): 返回一个包含所有捕获组的元组。.start(): 返回匹配的起始索引。.end(): 返回匹配的结束索引。.span(): 返回一个包含起始和结束索引的元组(start, end)。
示例 4:提取所有标题,并获取其位置
假设我们要从一个 HTML 片段中提取所有 <h1> 标签内的内容。

import re
html_text = """
<h1>欢迎来到我的网站</h1>
<p>这是一个段落。</p>
<h1>关于我们</h1>
<div>一些其他内容</div>
<h1>联系方式</h1>
"""
# <h1>(.*?)</h1>
# <h1> 匹配字面上的 "<h1>"
# (.*?) 这是一个捕获组
# . 匹配除换行符外的任意字符
# * 匹配前面的元素零次或多次
# ? 使 * 变成“非贪婪模式”,即匹配尽可能少的字符,这样可以正确处理多个标签
# </h1> 匹配字面上的 "</h1>"
pattern = r"<h1>(.*?)</h1>"
# 使用 finditer 获取迭代器
for match in re.finditer(pattern, html_text):
# 获取整个匹配的文本
full_match = match.group(0)
print(f"完整匹配: {full_match}")
# 获取第一个捕获组的内容(也就是标题文本)text = match.group(1)
print(f"提取标题: {title_text}")
# 获取标题在原文中的位置
start_pos = match.start()
end_pos = match.end()
print(f"位置: {start_pos} - {end_pos}")
print("-" * 20)
# 输出:
# 完整匹配: <h1>欢迎来到我的网站</h1> 欢迎来到我的网站
# 位置: 0 - 19
# --------------------
# 完整匹配: <h1>关于我们</h1> 关于我们
# 位置: 33 - 43
# --------------------
# 完整匹配: <h1>联系方式</h1> 联系方式
# 位置: 62 - 72
# --------------------
编译正则表达式:re.compile()
如果你的同一个正则表达式要在循环中被多次使用(例如处理一个大文件的每一行),为了提高效率,可以预先将正则表达式编译成一个模式对象。
import re
# 预编译正则表达式
# 编译后的对象可以重复使用,效率更高
phone_pattern = re.compile(r"(\d{3})-(\d{8})")
text_lines = [
"我的号码是 010-12345678",
"请拨打 021-87654321 联系我们",
"这个不是电话 123-456"
]
for line in text_lines:
# 使用编译后的对象进行匹配
match = phone_pattern.search(line)
if match:
# match 对象的方法和之前一样
area_code = match.group(1)
number = match.group(2)
print(f"在行 '{line}' 中找到电话: 区号 {area_code}, 号码 {number}")
# 输出:
# 在行 '我的号码是 010-12345678' 中找到电话: 区号 010, 号码 12345678
# 在行 '请拨打 021-87654321 联系我们' 中找到电话: 区号 021, 号码 87654321
重要技巧与注意事项
原始字符串 r"..."
在 Python 中定义正则表达式时,强烈建议在字符串前加上 r,r"\d+"。
- 原因:反斜杠
\在普通字符串中是转义字符(如\n换行),在正则表达式中,\也是特殊字符(如\d表示数字),如果不用原始字符串,你就需要写成"\\d+",第一个\转义了第二个\,才能表示一个字面上的\,使用原始字符串可以避免这种混乱。
贪婪模式 vs. 非贪婪模式
-
贪婪模式(默认):, , ,
{m,n}等量词会匹配尽可能多的字符。text = "<div>内容1</div><div>内容2</div>" pattern = r"<div>(.*)</div>" # 贪婪模式 print(re.findall(pattern, text)) # 输出: ['内容1</div><div>内容2'] # 错误!它匹配了从第一个 <div> 到最后一个 </div> 之间的所有内容
-
非贪婪模式:在量词后加上 ,会匹配尽可能少的字符。
text = "<div>内容1</div><div>内容2</div>" pattern = r"<div>(.*?)</div>" # 非贪婪模式 print(re.findall(pattern, text)) # 输出: ['内容1', '内容2'] # 正确!
在处理嵌套标签或结构时,非贪婪模式 () 通常是更安全的选择。
常用正则表达式速查表
| 模式 | 描述 | 示例 |
|---|---|---|
| 匹配除换行符外的任意单个字符 | a.c 匹配 "abc", "aac" |
|
\d |
匹配任意数字,等同于 [0-9] |
\d{3} 匹配 "123" |
\D |
匹配任意非数字字符 | \D 匹配 "a", " " |
\w |
匹配“单词”字符,[a-zA-Z0-9_] |
\w+ 匹配 "hello", "Python3" |
\W |
匹配非“单词”字符 | \W 匹配 "!", "@" |
\s |
匹配任意空白字符(空格, tab, 换行等) | \s+ 匹配 " ", "\t\n" |
\S |
匹配任意非空白字符 | \S 匹配 "a", "1" |
[...] |
定义一个字符集 | [aeiou] 匹配任意一个元音字母 |
[^...] |
定义一个反向字符集(不匹配其中的字符) | [^0-9] 匹配任意非数字字符 |
| 匹配前面的元素 0 次或多次 | ab*c 匹配 "ac", "abc", "abbc" |
|
| 匹配前面的元素 1 次或多次 | ab+c 匹配 "abc", "abbc" (不匹配 "ac") |
|
| 匹配前面的元素 0 次或 1 次 | ab?c 匹配 "ac", "abc" |
|
{n} |
精确匹配 n 次 | \d{3} 匹配 3 个数字 |
{n,} |
至少匹配 n 次 | \d{2,} 匹配 2 个或更多数字 |
{n,m} |
匹配 n 到 m 次 | \d{2,4} 匹配 2, 3 或 4 个数字 |
| 捕获组,用于提取部分内容 | (\d{3})-(\d{4}) |
|
^ |
匹配字符串的开头 | ^Hello 匹配 "Hello world" |
| 匹配字符串的结尾 | world$ 匹配 "Hello world" |
|
\| |
或(逻辑或) | cat\|dog 匹配 "cat" 或 "dog" |
| 你的需求 | 推荐方法 | 示例 |
|---|---|---|
| 提取所有匹配的简单文本 | re.findall() |
re.findall(r"\d+", text) |
| 从匹配项中提取特定部分 | re.findall() + 捕获组 |
re.findall(r"(\w+)@(\w+)", text) |
| 需要获取匹配项的位置信息 | re.finditer() + Match 对象 |
for m in re.finditer(...): m.span() |
| 在循环中重复使用同一个正则 | re.compile() 预编译 |
pattern = re.compile(r"..."); pattern.search(text) |
希望这份详细的指南能帮助你熟练掌握 Python 中使用正则表达式提取字符串的技巧!
